Python3的面向对象编程

原创
2019/06/22 01:35
阅读数 82

Python3的面向对象编程

面向对象OOP
全称Object Oriented Programming
对象作为基本单元,将数据和功能封装在里面.
实现很好的,复用性,灵活性扩展性.
  • 面向对象的概念
面向对象时一种抽象
这里的抽象指的是,用分类的眼光看待事物的一种方法.
对一些事物进行总结,分析他们的共性,利用抽象.
能大大简化在实际中解决问题的难度.

面向对象包含两个基本的概念:
    类: 定义了事物的抽象特点
    对象: 对象时类的实例
    
面向对象的要素:
    封装,继承,多态

常用系统函数

helo()          获取某对象的帮助文档
@classmethod    声明该方法为类方法,类方法也可以像静态方法那样调用,但是还是会走构造函数.
@staticmethod   声明该方法为静态方法,静态方法的原理和PHP一样,不走构造函数.
delattr()       删除指定对象的属性
setattr()       给对象某属性赋值,属性不存在则创建
getattr()       获取指定对象的指定属性
hasattr()       该对象是否有该属性
dir()           查看函数,查看类的所有方法和属性
globals()       获取当前全局函数与对象
locals()        获取当前局部函数与对象
hash()          获取某对象的hash值
id()            获取某对象的id
isinstance()    对象是否是该类的实例
issubclass()    该类(前)是否是该类(后)的子类(包括自己)
repr()          将对象转为字符串表达形式,类似于PHP的对象序列化
super()         使用该方式继承父类,可以避免钻石继承问题

定义类

  • 定义类
# 定义类
    # 老式类和新式类,通过内嵌函数type()很容易对比出差异,包括类名都有所不同
    # 老式类,没有继承Object,建议不要用了
    class 类名:
    # 新式类,继承object,会继承很多新的属性和方法,在python3所有类都是新式类.
    # 当没有要继承的父类时,应该继承object.object是一切类的父类
    class 类名(object):
    
# 构造函数
def __init__(self, ...):

# 析构函数
def __del__(self, ..):

# 内嵌函数
    # 返回一个对象的属性
    dir()
    # 获取对象的类型
    type()

  • 定义类的属性
# 定义一个类同时在,类中定义属性
class Liuhao(object):
    sex='male'
    
# 在构造函数中定义属性,类一旦被实例化同时传入值,就会该设置属性,同时也可以自定义属性的值
class Liuhao(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

# 访问控制
    不好意思,Python本身没有提供访问控制
    所以需要通过自建格式,提醒自己要自觉.
    下面是约定俗称的,格式
    class Liuhao(object):
    def __init__(self,name,age):
        self.name = name        # 公开, 类似PHP中的public
        self._age = age         # 私有属性,类似PHP的private
        self.__weight=weight    # 受保护的,类似PHP中的protected
    注意__会改变属性名称,类内的调用不受影响.
    但是在类的外部调用时,__weight会变成_Liuhao__weight
    实例化该类后, 通过XXX.Liuhao.__dict__可以得知这一点.


# 获取对象所有的属性情况,键值对应
className.__dict__ 
  • 定义类的方法
函数 只是一块代码提供调用
方法 依附于一个类

Python中一切结对象
类的方法也算是类的一种属性
类的方法可以重新赋值为一个变量,如:字符串.
这时候在输出,就会发现,这个方法就是一个字符串.
如:
    def Test(object):
        def test(self):
            # 占位符
            pass
    # 实例化该类
    t = Test()
    print(t.test)
    <bound method Test.test of <__main__.Test object at 0x1081af198>>
    
    t.test=111
    print(t.test)
    结果为111

方法的访问控制也是基本没有
    还是要靠自觉
    class Example(object):
        # 共有方法
        def add(self):
            pass
            
        # 私有方法
        def _minus(self):
            pass
        
        # 受保护的,和属性篇的一样,只是改了个名字而已
        def __multiply(self):
            pass


# 装饰器 @classmethod
调用的的时候用类名,而不是某个对象

# 装饰器 @property
像访问属性一样调用方法

具体用法如下:
    class Programer(object):
        hobby='Player Computer'
        
        def __init__(self,name,age,weight):
            self.name = name
            self._age = age
            self.__sex = weight
    
        @classmethod
        def get_hobby(cls):
            return cls.hobby
        
        @property
        def get_weight(self):
            return self.__wight
    
        def self_introduction(self):
            return "My Name is %s \n I'm %s years old \n" % (self.name,self.age)
            

if __name__ =='__main__':
    # 实例化,同时传参
    programer = Programer('Albert',25,80)
    # 打印对象所有的属性,包括继承的
    print(dir(programer))
    # 打印对象中设置的属性,以键值对应的方式
    print(programer.__dict__)
    # 直接用类名调用类的方法
    print(Programer.get_hobby())
    # 通过访问属性的形式调用该对象
    print(programer.get_weight)
    # 正常调用,对象里的方法
    programer.self_introduction()


  • 类的继承
定义类的继承,如果没有要继承的,就继承object,object是一切类的父类
class DerivedClassName(BaseClassName):
继承会继承父类的属性和方法
子类的方法/属性名和父类方法/属性名相同时,会重写父类的方法.

在子类重,重写了父类的方法,同时又想在子类中该方法调用父类中的改方法
需要用到关键字super或者使用父类的名称调用
如下:
    class A(object):
        def method(self, arg):
            pass
            
    class B(A):
        def method(self, arg):
            # 调用被重写的父类中的该方法
            super(B, self).method(arg)
            # 或者,这样的方式,没有提现,类的继承关系
            A.method(arg)

子类类型的判断
# 判断父类的类型
isinstance
# 判断是否是子类
issubclass

多继承
class DerivedClassName(Base1,Base2,Base3):


# 继承的案例

    class Programer(object):
        hobby='Player Computer'
        
        def __init__(self,name,age,weight):
            self.name = name
            self._age = age
            self.__sex = weight
    
        @classmethod
        def get_hobby(cls):
            return cls.hobby
        
        @property
        def get_weight(self):
            return self.__wight
    
        def self_introduction(self):
            return "My Name is %s \n I'm %s years old \n" % (self.name,self.age)
 
 # 继承父类
class BackendProgramer(Programer):
    # 重写父类的构造函数,同时多加入一个形参
    def __init__(self,name,age,weight,language);
        # 调用父类,同时传入参数,设置父类的属性
        super(BackendProgramer, self).__init(name,age,weight)
        # 设置父类中没有,而子类中有的属性
        self.language = language
    


if __name__ =='__main__':
    # 实例化,同时传参
    programer = BackendProgramer('Albert',25,80,'Python')
    # 打印对象的所有属性,包括继承的
    print(dir(programer))
    # 打印对象中设置的属性,以键值对应的方式,
    # 发现,输出了父类和子类的构造函数中设置的所有属性
    print(programer.__dict__)
    # 判断对象的种类
    print(type(programer))
    # 判断Programer是不是他的父类
    print(isinstance(programer,Programer))

  • 类的多态
做同一件事情,有不同的流程和反馈.就是属于多态.
多态要素:
    1.继承
    2.方法重写
    


# 多态的案例


    class Programer(object):
        hobby='Player Computer'
        
        def __init__(self,name,age,weight):
            self.name = name
            self._age = age
            self.__sex = weight
    
        @classmethod
        def get_hobby(cls):
            return cls.hobby
        
        @property
        def get_weight(self):
            return self.__wight
    
        def self_introduction(self):
            return "My Name is %s \n I'm %s years old \n" % (self.name,self.age)
 
 # 继承父类
class BackendProgramer(Programer):
    # 重写父类的构造函数,同时多加入一个形参
    def __init__(self,name,age,weight,language);
        # 调用父类,同时传入参数,设置父类的属性
        super(BackendProgramer, self).__init(name,age,weight)
        # 设置父类中没有,而子类中有的属性
        self.language = language
        
    # 重写父类的self_introduction
    def self_introduction(self):
        return "My Name is %s \n My favorite language is %s \n" % (self.name,self.language)


def introduce(programer):
    # 判断参数是否是Programer的子类
    if isinstance(programer,Programer):
        programer.self_introduction()



if __name__ =='__main__':
    # 实例化,同时传参
    programer = Programer('Albert',25,80)
    backend_programer = BackendProgramer('Tim',30,70,'Python')
    # 调用该函数,进行调用,父类
    introduce(programer)
    # 调用该函数进行调用子类
    introduce(backend_programer)


# 由此可见
多态的好处,正是因为多态的特性使得添加功能的时候非常的简单.
introduce只要保证,传参是Programer的子类,就调用self_introduction方法即可
不管参数具体是哪个类,哪个子类
    

Python魔术方法

魔术方法就是对类的定制

大部分的内嵌函数,都是有相应的魔术方法支持的,
如果想让类的方法,使用很多内嵌方法
可以定义相应的魔术方法,使得对象支持这些函数的调用


定义魔术方法前后有两条下划线
# 如构造方法,在垃圾回收时触发
def __init(self):
# 如析构方法
de __del__(self):
# 创建类的对象,是在__init__之前调用的
# __new__ 是返回一个初始对象
def __new__(cls):


# __new__ 和__init__的使用案例
class Programer(object):
    
    # 重写父类的new
    def __new__(cls,*args,**kwargs):
        print('call__new__method')
        print(args)
        # 调用父类的new方法,来返回对象
        return super(Programer,cls).__new__(cls,*args,**kwargs)
        
    # 构造函数
    def __init__(self,name,age):
        print('call__init_method')
        self.name = name
        self.age = age
        
        
if __name__ == '__main__':
    programer = Programer('Albert',25)
    # 打印所有,类内设置的属性,以键值对应的形式
    print(programer.__dict__)
    

  • 类与运算符
# 通过下面的输出,一方面我们知道了,Python一切皆对象
# 另一方面__lt__等等,魔术方法可以知道,怪不得支持运算
>>> t = 'test'
>>> t == t
True
>>> dir(t)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']


比较运算符

# 包含了两个对象比较的所有情况,大小等于
__cmp__(self,other)
# 等于
__eq__(self,other)
# 小于
__lt__(self,other)
# 大于
__gt__(self,other)

数字运算符
# 加法
__add__(self,other)
# 减法
__sub__(self,other)
# 乘法
__nul__(self,other)
# 除法
__div__(self,other)

逻辑运算
# 或
__or__(self,other)
# 与
__and__(self,other)



# 案例

class Programer(object):

    # 构造函数
    def __init__(self,name,age):
        self.name=name
        if isinstance(age,int):
            self.age=age
        else:
            raise Exception('age must be int')
    
    # 重写,判断是否相等, 通过age来判断的   
    def __eq__(self,other):
        if isinstance(other,Programer):
            if self.age==other.age:
                return True
            else :
                return False
        else:
            raise Excetion('The type of object must be Programer')
   
   # 重写,加法运算, 相加的也是age
    def __add__(self,other):
        if isinstance(other,Programer):
            return self.age + other.age
        else:
            raise Exception('The type of object must be Programer')
            
if __name == '__main__:
    p1 = Programer('Alber',10)
    p2 = Programer('Tim',20)
    print(p1 == p2)
    print(p1 + p2)

  • 类的展现相关的模式方法
也就是说,如果一个对象,可以被相应方法转换为字符串的话
那么他的类里面肯定是在类里面定义了,或者是,继承了相应的魔术方法


# 是把对象转换为适合人看的方式,不可以通过eval()运行
__str__
# 是把对象转换为适合机器运行的方式
# 该方法转换之后可以通过eval()来直接作为Python运行
__repr__
# 和上面两种是两种不同的对象
__Unicode__


# 展现对象的属性的内嵌方法,与之对应的对象里面就有一个__dir__模式方法来控制
# 调用__dir__的输出结果
# 比如某些时候不想展示,某些属性,就可以重写该方法
__dir__


# 案例

class Programer(object):
    # 构造函数
    def __init__(self,name,age):
         self.name=name
        if isinstance(age,int):
            self.age=age
        else:
            raise Exception('age must be int')
    
    # 重写
    def __str__(self):
        return '%s is %s years old' % (self.name,self.age)
        
    # 重写类的属性展示方法
    def __dir__(self):
        return self.__dict__.keys()
        

if __name == '__main__':
    p = Programer('alber',20)
    # 通过对__str__的注释可以发现两者的巨大区别
    # print()其中有一个就是使用__str__将对象转换为字符串
    print(p)
    # 通过对__dir__的注释可以发现两者的巨大区别
    print(dir(p))

    
个人理解:
    基本上所有的内嵌函数,在对象中都有相应的魔术方法.
    当我们通过内涵函数,处理对象的时候,
    如: print(className的实例的时候),就会调用该对象的魔术方法__str__
        进行处理.也可能是你没有定义该魔术方法,但是继承的父类里面有.

  • 类的属性访问
Python的类本身没有访问控制
如果只是用__来定义属性,他也只是在属性前面加上类的名字.
并不会阻止访问.
但是通过魔术方法,可以给属性添加想要的访问控制.


设置对象属性
__setattr__(self,name,value):

# 使用方式

# 错误的使用犯法会导致无限递归
# 因为在于__setattr__已经重写的setattr
# 这时候再去调用内嵌函数setattr(),又会进入这个区间
# Python对无限递归限制为默认1000次,超过1000次就会报错
def __setattr__(self,name,value):
    setattr(self,name,value)
    
# 正确的使用方法
def __setattr__(self,name,value):
    self.__dirct__[name] = value



# 查询对象数据

# 在访问默认情况下没有被调用到的情况下
__getattr__(self,name):

# 在每次访问属性时都会被调用到,容易引起无限递归,使用的时候要小心
__getattribute__(self,name):


# 删除对象的属性
__delattr__(self,name):


# 案例

class Programer(object):
    
    # 构造函数
    def __init__(self,name,age):
        self.name = name
        self.age = age
    
    # 获取属性,每次获取都会调用
    def __getattribute__(self,name):
        # 无限递归
        # return getattr(self,name)
        # 无限递归,其实说明__dict__获取时,他也会调用到__getattribute__方法
        # return self.__dict__['name']
        # 推荐写法,最好调用父类的方法,不会出现问题
        return super(Programer, self).__getattribute__(name)
        
    # 设置属性
    def __setattr__(self,name,value):
        # 无限递归
        # setattr(self,name,value)
        # 推荐写法
        self.__dict__['name'] = value
    
    
if __name__ == '__main__':
    p = Programer('Alter',  20)
    print(p.name)



展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部