三个魔术方法:
__get__()
__set__()
__delete__()
object.__get__(self,实例名,owner) #owner = 属主 ,instance = 属主类owner的实例
object.__set__(self,实例名,value)
object.__delete__(self,实例名)
更改属性的行为,当属性等于实例的时候,则可以进行操作
例:
1234567 | class A: def __init__( self ): self .a1 = 'a1' class B: x = A() def __init__( self ): pass |
这样是可以执行的,首先定义好了A
通过定义B的x属性,调用A()
相当于在B类中执行:
123 | print (A().a1) x = A() print (x.a1) |
这两个是等价的
标记执行顺序
12345678910111213141516171819202122232425 | class A: def __init__( self ): print ( 'init' ) self .a1 = 'a1' class B: x = A() def __init__( self ): print ( 'B init' ) print (B.x.a1) init a1 class A: def __init__( self ): print ( 'init' ) self .a1 = 'a1' class B: x = A() def __init__( self ): print ( 'B init' ) self .x = 100 b = B() print (B.x.a1) init B init a1 |
涉及到字典的执行顺序,所以,print(b.x.a1)是不行的
12 | print (b.x.a1) AttributeError: 'int' object has no attribute 'a1' |
引入描述器
_
12345678910111213 | __get__( self ,instance,owner) class A: def __init__( self ): print ( 'A init' ) self .a1 = 'a1' def __get__( self , instance, owner): print ( self ,instance,owner) class B: x = A() #A() 就是一个描述器,当对B()或B的实例的x的属性进行访问,则成为A()的实例的方式,则调用__get__方法 def __int__( self ): print ( 'B init' ) self .x = 100 print (B.x.a1) |
发现报错提示如下:
print(B.x.a1)
AttributeError: 'NoneType' object has no attribute 'a1'
提示 None类型是不能调用的,当通过一个属性访问,如果属性是另一个类的实例,而恰好这个类又实现了描述器的方法之一
当访问描述器的时候,如果是get触发则返回当前实例以及描述器属主的类型信息
所以,return返回为None的实例,则不能被调用
打印B.x 的类型,可看到为None
12345 | print (B.x) def __get__( self , instance, owner): print ( self ,instance,owner) <__main__.A object at 0x0000000000B88390 > None < class '__main__.B' > None |
对B实例化后打印查看
1234 | print ( 'B.x : ' ,B.x) print () b = B() print ( 'b.x.a1 : ' ,b.x.a1) |
返回如下:
1234567 | A init Traceback (most recent call last): <__main__.A object at 0x0000000000DB80B8 > None < class '__main__.B' > B.x : None print ( 'b.x.a1 : ' ,b.x.a1) <__main__.A object at 0x0000000000DB80B8 > <__main__.B object at 0x0000000000DB83C8 > < class '__main__.B' > AttributeError: 'NoneType' object has no attribute 'a1' |
发现依旧被拦截,所调用的是一个None类型
归根结底,都是与类属性有关系
12 | b = B() print (B.x) |
返回如下
123 | A init <__main__.A object at 0x0000000000718390 > None < class '__main__.B' > None |
对照get定义的方法:
12 | def __get__( self , instance, owner): print ( self ,instance,owner) |
执行效果如下:
123 | A init <__main__.A object at 0x0000000000718390 > None < class '__main__.B' > None |
原来的实例返回是None,通过get方法变为了类的属性
1234 | b = B() print (B.x) print ( '-' * 90 ) print (b.x.a1) |
凡是进入描述器的三个方法之一,都是会被拦截进行操作
返回如下所示:
123456 | A init <__main__.A object at 0x0000000000858390 > None < class '__main__.B' > None - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 分别返回了 self , instance, owner <__main__.A object at 0x0000000000858390 > <__main__.B object at 0x0000000000836320 > < class '__main__.B' > |
当一个类的类属性等于另一个类的实例的时候,则实现了描述器方法,则是描述器的类
如果是类属性上访问的话,直接触发拦截
如果是实例属性访问,则不会访问描述器方法触发
解决返回值问题:return self
12345678910111213 | class A: def __init__( self ): print ( 'A init' ) def __get__( self ,instance,owner): print ( 'A get' , self ,instance,owner) return self class B: x = A() def __init__( self ): print ( 'B.init' ) # print(B.x) b = B() print (B.x) |
返回如下:
1234 | A init B.init A get <__main__.A object at 0x0000000000DA6518 > None < class '__main__.B' > <__main__.A object at 0x0000000000DA6518 > |
如果只是获取当前属性的手段,通过属性的描述器可以操作属主
这样可以解决不能访问的弊端
在遇到get中应该return一个有意义的值,至于return什么值合适,需要后期定义,具体就是可以获取属主的类及属性
如果仅实现了__get__,就是非数据描述符
同时实现了__set__ + __get__ 就是数据描述符
对日常来讲重要的是get和set同时出现
如果不是访问类的属性的话,则不会触发任何效果,只能是实例才会被拦截
__set__ 方法
123456789101112131415 | class A: def __init__( self ): print ( 'A init' ) self .a1 = 'a1' def __get__( self ,instance,owner): print ( '!!!B get' , self ,instance,owner) return self def __set__( self ,instance,value): # #加入set之后,这里原本是为实例设置,但是触发了set print ( '~~A.set' , self ,instance,value) class B: x = A() def __init__( self ): self .x = 100 b = B() print (b.x) |
可以看到,首先被__set__方法先拦截
1234 | A init ~~A. set <__main__.A object at 0x0000000000DD45C0 > <__main__.B object at 0x0000000000DB7320 > 100 !!!B get <__main__.A object at 0x0000000000DD45C0 > <__main__.B object at 0x0000000000DB7320 > < class '__main__.B' > <__main__.A object at 0x0000000000DD45C0 > |
对b.x进行跟进
12345678910111213 | class A: def __init__( self ): print ( 'A init' ) self .a1 = 'a1' def __get__( self ,instance,owner): print ( '!!!B get' , self ,instance,owner) return self def __set__( self ,instance,value): print ( '~~A.set' , self ,instance,value) class B: x = A() def __init__( self ): self .x = 10 |
对每个函数进行标记并跟进:
123 | b = B() A init ~~A. set <__main__.A object at 0x0000000000A945C0 > <__main__.B object at 0x0000000000A77320 > 100 |
当访问x属性,直接在A()中被__get__拦截
12 | print (b.x) !!!B get <__main__.A object at 0x0000000000DA45C0 > <__main__.B object at 0x0000000000D87320 > < class '__main__.B' |
查看类型字典
1234 | print (b.__dict__) {} print (B.__dict__) { 'x' : <__main__.A object at 0x0000000000D77588 >, '__weakref__' : <attribute '__weakref__' of 'B' objects>, '__doc__' : None , '__module__' : '__main__' , '__init__' : <function B.__init__ at 0x0000000000DDAAE8 >, '__dict__' : <attribute '__dict__' of 'B' objects>} |
看到没有dict内容
照常来讲会修改dict,但是触发了set描述器,也就self.x = 这条语句没有被加入到dict
总结:
set如果对实例化中的属性定义,则对属性做修改
说到底就是如果实例的字典里没有,则去类的dict中去查找,set是对类的dict进行修改
通过这样的方式绕开了字典搜索
官方解释:有set,实例的优先级最高,如果没有set则类的优先级比较高
总结:
get:
12345678910111213141516171819 | class A: def __init__( self ,value = 'abc' ): self .a1 = value def __get__( self ,instance,owner): return self class B: x = A() def __init__( self ): self .x = A( 123 ) print (B.x) print (B.x.a1) <__main__.A object at 0x0000000000DB84A8 > abc print (b.x.a1) 123 print (B.x.a1) ~~~~A__get__ <__main__.A object at 0x0000000000DA84A8 > None < class '__main__.B' > abc |
print(b.__dict__),发现实例的dict中存在x方法
1234 | { 'x' : <__main__.A object at 0x00000000006F7940 >} print (B.__dict__) { '__init__' : <function B.__init__ at 0x0000000000E2AA60 >, '__weakref__' : <attribute '__weakref__' of 'B' objects>, 'x' : <__main__.A object at 0x00000000006F70F0 >, '__dict__' : <attribute '__dict__' of 'B' objects>, '__doc__' : None , '__module__' : '__main__' } set : |
只要设置相关的属性,实例方法添加不上dict,而set优先级别高,可以看到都是针对A的对象
print(b.x.a1) 被set先拦截
1234567891011 | !!!!A__set__ <__main__.A object at 0x0000000000746550 > <__main__.B object at 0x0000000000727278 > <__main__.A object at 0x00000000007272B0 > ~~~~A__get__ <__main__.A object at 0x0000000000746550 > <__main__.B object at 0x0000000000727278 > < class '__main__.B' > abc print (B.x.a1) !!!!A__set__ <__main__.A object at 0x0000000000726550 > <__main__.B object at 0x0000000000707278 > <__main__.A object at 0x00000000007072B0 > ~~~~A__get__ <__main__.A object at 0x0000000000726550 > None < class '__main__.B' > abc print (b.__dict__),发现实例的 dict 中不存在方法 {} print (B.__dict__) { 'x' : <__main__.A object at 0x0000000000DB7518 >, '__module__' : '__main__' , '__init__' : <function B.__init__ at 0x0000000000E1BAE8 >, '__weakref__' : <attribute '__weakref__' of 'B' objects>, '__doc__' : None , '__dict__' : <attribute '__dict__' of 'B' objects>} |
一句话总结:一旦使用set,只能操作类属性
下面例子中,虽然会触发set,但是什么都没有操作
1234 | b = B() b.xxx = 777 !!!!A__set__ <__main__.A object at 0x0000000000BE6550 > <__main__.B object at 0x0000000000BC7278 > 777 { 'xxxx' : 777 } |
再访问的时候,再将实例返回回来,get就进行操作了
本质
主要看字典,一点点看到底修改了哪些,通过实例的方式无法修改属性
主要的特点是把实例从__dict__中去掉了,造成了该属性如果是数据描述则优先访问的假象
说到底,属性访问顺序就从来没有变过
一句话总结:非数据描述器可以覆盖,数据描述器直接修改类
在py中,所有的方法都是数据描述器
实现一个static装饰器
静态方法的本质
全局函数放到类中,使用时候,通过我们的类对象进行使用
123456789 | class A: @ staticmethod def bar(): return 1 def test( self ): return 2 f = A() print (f.test) print (f.bar) |
查看结果
12 | <bound method A.test of <__main__.A object at 0x0000000000D86278 >> <function A.bar at 0x0000000000DF11E0 > |
静态方法是作为一个function传递进来的
首先我们搞明白需求 如何调用的 A.foo 这么调用
基础框架
12345678910 | class StaticMethod : def __init__( self ,fn): self .fn = fn def __get__( self ,instance,owner): print ( self ,instance,owner) class A: @ StaticMethod def foo(): print ( 'static' ) print (A.__dict__) |
调用返回None,因为没有A的实例
123 | a = A.foo print (a) None |
相当于在定义foo的时候被传递给StaticMethod(foo)
当前的foo相当于一个实例对象
返回的东西加了括号才可以调用,所以必须返回self
12345678910111213 | class Static_Method: def __init__( self ,fn): print ( 'fn:' ,fn) self .fn = fn def __get__( self ,instance,owner): print ( self ,instance,owner) return self .fn class A: @Static_Method def foo(): print ( 'static' ) f = A.foo print ( 'f:' ,f) |
这个foo原封不动的返回,打印他们的内存地址查看
123 | fn: <function A.foo at 0x0000000000DEAA60 > <__main__.Static_Method object at 0x0000000000A764E0 > None < class '__main__.A' > f: <function A.foo at 0x0000000000DEAA60 > |
等价式:foo = Static_Method(foo)
就是说,调用的时候,必须以func类型传递到Statice_Method中
12345 | class A: # @Static_Met def foo(): print ( 'static' ) print (A.foo) |
返回为:
<function A.foo at 0x0000000000E3F9D8>
共同学习,写下你的评论
评论加载中...
作者其他优质文章