9.面向对象进阶
Posted xixi18
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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__()
以上是关于9.面向对象进阶的主要内容,如果未能解决你的问题,请参考以下文章
Python全栈--9.1--面向对象进阶-super 类对象成员--类属性- 私有属性 查找源码类对象步骤 类特殊成员 isinstance issubclass 异常处理