9.面向对象进阶

一.isinstance(obj,cls)和issubclass(sub,super)
(1)isinstance(obj,cls)检查一个对象obj是否是一个类cls的实例
(2)issubclass(sub,super)检查sub类是否是super类的派生类

class Foo:                  #定义一个类Foo
    pass

class Bar(Foo):             #定义一个类Bar继承了Foo
    pass

f1=Foo()                     #实例化得到f1这个对象
print(isinstance(f1,Foo))    #f1是对象,Foo是类,isinstance判断f1这个对象是否由Foo这个类实例化而来
print(issubclass(Bar,Foo))   #Bar是对象,Foo是类,issubclass判断Bar是否是Foo的子类

返回:
True
True
二.__getattr__和__getattribute__二者同时出现,只会执行__getattribute__,除非__getattribute__在执行中抛出异常AttributeError

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):               #是在__getattribute__里抛出AttributeError异常的时候才会触发__getattr__的运行
        print('执行的是getattr')

    def __getattribute__(self, item):          #不管属性找得到找不到首先触发__getattribute__运行,属性存在会从当前实例属性字典里返回想要的值,如果不存在会报异常
         print('执行的是getattribute')
         raise AttributeError('抛出异常了')   #AttributeError这句话模拟python自动抛出的异常会把指令发给__getattr__,如果没有__getattr__程序会崩掉
f1=Foo(10)
f1.xxxxxx                                        #调用不存在的属性xxxxxx

返回:
执行的是getattribute
执行的是getattr
三.__setitem__,__getitem__,__delitem__跟字典相关的查询,赋值,删除

class Foo:                                #定义一个类
    def __getitem__(self, item):          #查询
        print('getitem',item)
        return self.__dict__[item]       #self调用他的属性字典__dict__,[item]找那个属性

    def __setitem__(self, key, value):    #赋值
        print('setitem')
        self.__dict__[key]=value          #self调用他的属性字典__dict__,[key]就是中括号传过来的值name,value是xixi

    def __delitem__(self, key):           #删除
        print('delitem')
        self.__dict__.pop(key)            #self调用他的属性字典__dict__,pop(key)指定删除

f1=Foo()                                   #实例化得到f1这个对象
print(f1.__dict__)                         #查看f1实例字典,没定义init方法f1字典里是空:{}
#以字典方式赋值
f1['name']='xixi'                        #会触发 __setitem__函数运行--->f1.__dict__['name']='xixi'
print(f1.__dict__)                         #查看f1实例字典,赋值成功:'name': 'xixi'}
#以字典方式查询:
print(f1['name'])                         #可以调到name的value返回:getitem name 和 xixi
#以字典方式删除
del f1['name']
print(f1.__dict__)                         #返回:delitem 和 {}

打印:
{}
setitem
{'name': 'xixi'}
getitem name
xixi
delitem
{}
四.__str__和__repr__用来控制输出,输出的解法都是自己return的值,return的值必须是字符串
1.__str__函数或者print函数实际触发实例下面的__str__()方法

class Foo:
    def __init__(self,name,age):   #init方法传name,age
        self.name=name
        self.age=age

    def __str__(self):                                       #__str__调用的是f1.__str__()方法,必须要return个值
        return '名字是%s 年龄是%s' %(self.name,self.age)  #可以自己控制str的打印的信息

f1=Foo('xixi',18)                  #实例化传俩个参数
#方式一:
print(f1)                           #实际触发的是系统提供的str(f1)方法的运行,str(f1)执行的是f1.__str__()
#方式二:
x=str(f1)
print(x)
#方式三:
x=f1.__str__()
print(x)

返回:
名字是xixi 年龄是18
名字是xixi 年龄是18
名字是xixi 年龄是18
2.__repr__或者交互解释器实际触发实例下面的__repr__()方法

class Foo:
    def __init__(self,name,age):   #init方法传name,age
        self.name=name
        self.age=age

    #def __str__(self):
    #    return '这是str'
    #
    def __repr__(self):            #在解释器里触发
        return '名字是%s 年龄是%s' % (self.name, self.age)

f1=Foo('xixi',18)                  #实例化传俩个参数
print(f1)                           #如果有str先找str(f1)下的f1.__str__(),如果找不到str直接找f1.__repr__()作为一个替代品

返回:
名字是xixi 年龄是18
五.__format__自定制格式化方法

format_dic={                                    #自定制格式化俩种日期输出格式
    'ymd':'{0.year}{0.mon}{0.day}',        #'ymd'对应的是'{0.year}{0.mon}{0.day}'格式
    'y-m-d':'{0.year}-{0.mon}-{0.day}',   #'y-m-d'对应的是'{0.year}-{0.mon}-{0.day}'格式
}
class Date:
    def __init__(self,year,mon,day):
        self.year=year
        self.mon=mon
        self.day=day

    def __format__(self, format_spec):          #自定义__format__方法,必须有返回值字符串
        print('开始执行')
        print('--->',format_spec)               #format_spec没有传参数就是空
        if not format_spec or format_spec not in format_dic:  #判断format_spec这个值如果是空not format_spec或者不在字典的key里format_spec not in format_dic
            format_spec = 'ymd'                 #给一个默认的格式是ymd
        fm=format_dic[format_spec]               #有了format_spec这个值就可以从字典里取到想要的值,fm就是取到了'{0.year}{0.mon}{0.day}'后面的格式
        return fm.format(self)                  #拿到格式就可以fm.format相当于.format(d1)
d1=Date(2018,10,'08')

print(format(d1,'ymd'))    #format把d1传给self,把ymd传给format_spec
print(format(d1,'y-m-d'))  #format把d1传给self,把y-m-d传给format_spec
print('不传format_spec打印默认格式',format(d1))
print('传的format_spec不在format_dic字典里打印默认格式',format(d1,'aaaa'))

返回:
开始执行
---> ymd
20181008
开始执行
---> y-m-d
2018-10-08
开始执行
--->
不传format_spec打印默认格式 20181008
---> aaaa
传的format_spec不在format_dic字典里打印默认格式 20181008
六.__slots__属性
1.是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
4.当你使用__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这个跟元祖列表很类似
5.缺点:不能在给实例添加新的属性,只能使用在__slots__中定义的那些属性名

class Foo:
    __slots__=['name','age']  #定义字符串类似属性字典的形式{'name':None,'age':None},只是定义了key,由这个类产生的实例不再具有__dict__属性字典

f1=Foo()                        #由Foo生成的实例只能有俩个定义的属性['name','age']
f1.name='xixi'
print(f1.name)
f1.age=18
print(f1.__slots__)   #显示f1可以定义的属性['name', 'age'],就是print(Foo.__slots__)类的属性

#f1.gender='male'     #给f1加一个没有的属性gender会报错

f2=Foo()              #实例化f2
print(f2.__slots__)   #f2没有类属性只有__slots__,f2也只能定义俩个属性['name', 'age']
f2.name='shishi'
f2.age=18
print(f2.name)
print(f2.age)

打印结果:
xixi
['name', 'age']
['name', 'age']
shishi
18
七.__doc__属性:在每一个类里面会加一个这个东西,只要调就是从自己的位置调

class Foo:
    pass

class Bar(Foo):
    pass

print(Bar.__doc__)  #该属性无法继承给子类
print(Bar.__doc__)  #该属性无法继承给子类

返回:
None
None
八.__module__和__class__
__module__表示当前操作的对象在那个模块
__class__表示当前操作的对象的类是什么
lib目录下有一个定义aa模块

class C:
    def __init__(self):
        self.name = 'xixi'

调用aa模块

from lib.xx import C  #调用lib目录下有一个aa模块,aa模块里有一个C类

c1=C()                 #实例化
print(c1.name)         #可以调用到

print(c1.__module__)   #查看c1来自那个模块
print(c1.__class__)    #查看c1来自那个类产生的

返回:
xixi
lib.xx
<class 'lib.xx.C'>
九__del__析构方法
当对象在内存中被释放时,自动触发执行

class Foo:
    def __init__(self,name):
        self.name=name

    def __del__(self):
        print('我执行啦')

f1=Foo('xixi')           #由Foo产生个实例传name

#del f1    #删除实例会触发__del__
#del f1.name #删除实例的属性不会触发__del__
print('--------------------->')
#程序运行完毕会自动回收内存,触发__del__

返回:
--------------------->
我执行啦
十.__call__
对象后面加括号,触发执行。

class Foo:                                 #创建一个类Foo
    def __call__(self, *args, **kwargs):   #定义__call__方法
        print('实例执行obj()')

f1=Foo()       #Foo()得到一个实例f1,Foo触发的call方法返回的实例

f1()           #f1加小括号调的是的这个对象Foo类下的__call__
#Foo()         #Foo也是一个对象Foo加小括号调的是的abc类下的__call__

返回:
实例执行obj()
十一.__next__和__iter__实现迭代器协议

class Foo:                      #创建一个Foo类
    def __init__(self,n):       #创造构造函数可以传值n
        self.n=n
    # __iter__
    def __iter__(self):         #加上__iter__方法变成把一个对象变成一个可迭代对象
        return self             #返回self
    #__next__
    def __next__(self):         #必须有一个next方法
        if self.n == 9:          #当n=9
            raise StopIteration('终止')  #StopIteration终止循环
        self.n+=1                #每次n这个值自增
        return self.n           #返回自增1这个值

f1=Foo(5)                        #得到f1这个对象

#for循环
for i in f1:  #for循环f1这个对象实际上先把这个对象变成f1.__iter__(),每次得到的结果是可迭代对象obj
     print(i)  #每次循环就是在调用obj下面的.__next_()方法

返回:
6
7
8
9
迭代器协议实现斐波那契数列

class Fib:                 #创建一个类Fib
    def __init__(self):    #创造构造函数
        self.a=1            #起始值a
        self.b=1            #起始值b

    def __iter__(self):     #定义__iter__方法
        return self        #return自己

    def __next__(self):     #定义 __next__方法
        if self.a > 50:     #当self.a大于50
            raise StopIteration('终止了')    #退出循环
        self.a,self.b=self.b,self.a + self.b   #self.a等于self.b,self.b等于self.a + self.b俩个相加的结果
        return self.a                         #返回self.a



f1=Fib()                                       #得到f1这个对象

print(next(f1))
print(next(f1))
print('==================================')
for i in f1:         #for循环从3以后开始
    print(i)

返回:
1
2
==================================
3
5
8
13
21
34
55
十二.描述符(__get__,__set__,__delete__)
1.描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()这三个方法中的一个,这也被称为描述符协议
__get__()调用一个属性时触发
__set__()为一个属性赋值时触发
__delete__()采用del删除属性时触发
2.描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的属性,不能定义到构造函数)

class Foo:                                     #定义新式类
    def __get__(self, instance, owner):        #定义__get__方法:一个对象调用属性的时候会触发
        print('===>get方法')

    def __set__(self, instance, value):        #定义__set__方法
        print('===>set方法')

    def __delete__(self, instance):            #定义__delete__方法
        print('===>delete方法')
#描述符必须要有另外一个类属性里定义才会触发
class Bar:                                    #定义一个Bar类
    x=Foo()                                   #在Bar类中定义一个类属性,这个类属性的值是Foo实例化的结果,类属性就是描述符的一个对象
b1=Bar()                                      #b1通过Bar得到一个实例
#调用
b1.x                                          #b1点x调用x这个属性就是Foo就可以触发描述符里的__get__方法:===>get方法
#赋值
b1.x=1                                        #b1点x=1调用x=1这个属性就是Foo就可以触发描述符里赋值的__set__方法:'===>set方法
#删除
del b1.x                                      #del b1点x调用x这个属性就是Foo就可以触发描述符里赋值的__delete__方法:'===>delete方法

返回:
===>get方法
===>set方法
===>delete方法
set方法的详解

class Foo:
    def __set__(self, instance, value):
        print('===>set方法',instance,value)   #打印一下
        instance.__dict__['x']=value           #操作实例下的属性字典进行了一个真正的赋值操作

class Bar:
    x=Foo()           #第三步:触发Foo()的 __set__方法
    def __init__(self,n):
        self.x=n      #第二步:相当于b1.x=10,x这个属性被代理了,赋值触发的是x=Foo()

b1=Bar(10)            #第一步:实例化操作self.x=n(这步实例化的过程相当于找的是 __set__)
print(b1.__dict__)

#修改x的值
b1.x=7777777
print(b1.__dict__)

返回:
===>set方法 <__main__.Bar object at 0x005747B0> 10
{'x': 10}
===>set方法 <__main__.Bar object at 0x005747B0> 7777777
{'x': 7777777}
3.描述符分俩种
(1)数据描述符:至少实现了__get__()和__set__()

class Foo:                                     #定义新式类
    def __get__(self, instance, owner):        #定义__get__方法:
        print('===>get方法')

    def __set__(self, instance, value):        #定义__set__方法
        print('===>set方法')

(2)非数据描述符:没有实现__set__()

class Foo:                                     #定义新式类
    def __get__(self, instance, owner):        #定义__get__方法:
        print('===>get方法')

4.注意:
(1)描述符本身应该定义成新式类,被代理的类也应该是新式类
(2)必须把描述符定义成这个类的属性,不能为定义到构造函数中
(3)要严格遵循该优先级,优先级由高到底分别是:类属性--->数据描述符--->实例属性--->非数据描述符--->找不到的属性出发__getatter__()

猜你喜欢

转载自www.cnblogs.com/xixi18/p/9816562.html