han8809 发表于 2018-8-3 10:42:43

python魔术方法之装饰器

  三个魔术方法:
  __get__()
  __set__()
  __delete__()
  object.__get__(self,实例名,owner)    #owner = 属主 ,instance = 属主类owner的实例
  object.__set__(self,实例名,value)
  object.__delete__(self,实例名)
  更改属性的行为,当属性等于实例的时候,则可以进行操作
  例:
class A:  
    def __init__(self):
  
      self.a1 = 'a1'
  
class B:
  
    x = A()
  
    def __init__(self):
  
      pass
  这样是可以执行的,首先定义好了A
  通过定义B的x属性,调用A()
  相当于在B类中执行:
print(A().a1)  
x = A()
  
print(x.a1)
  这两个是等价的
  标记执行顺序
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)是不行的
print(b.x.a1)  
AttributeError: 'int' object has no attribute 'a1'
  引入描述器
  _
__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
print(B.x)  
def __get__(self, instance, owner):
  
    print(self,instance,owner)
  
<__main__.A object at 0x0000000000B88390> None <class '__main__.B'>
  
None
  对B实例化后打印查看
print('B.x : ',B.x)  
print()
  
b = B()
  
print('b.x.a1 : ',b.x.a1)
  返回如下:
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类型
  归根结底,都是与类属性有关系
b = B()  
print(B.x)
  返回如下
A init  
<__main__.A object at 0x0000000000718390> None <class '__main__.B'>
  
None
  对照get定义的方法:
def __get__(self, instance, owner):  
    print(self,instance,owner)
  执行效果如下:
A init  
<__main__.A object at 0x0000000000718390> None <class '__main__.B'>
  
None
  原来的实例返回是None,通过get方法变为了类的属性
b = B()  
print(B.x)
  
print('-' * 90)
  
print(b.x.a1)
  凡是进入描述器的三个方法之一,都是会被拦截进行操作
  返回如下所示:
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
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)
  返回如下:
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__ 方法
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__方法先拦截
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进行跟进
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
  对每个函数进行标记并跟进:
b = B()  
A init
  
~~A.set <__main__.A object at 0x0000000000A945C0> <__main__.B object at 0x0000000000A77320> 100
  当访问x属性,直接在A()中被__get__拦截
print(b.x)  
!!!B get <__main__.A object at 0x0000000000DA45C0> <__main__.B object at 0x0000000000D87320> <class '__main__.B'
  查看类型字典
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:
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方法
{'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先拦截
!!!!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,但是什么都没有操作
b = B()  
b.xxx = 777
  
!!!!A__set__ <__main__.A object at 0x0000000000BE6550> <__main__.B object at 0x0000000000BC7278> 777
  
{'xxxx': 777}
  再访问的时候,再将实例返回回来,get就进行操作了
  本质
  主要看字典,一点点看到底修改了哪些,通过实例的方式无法修改属性
  主要的特点是把实例从__dict__中去掉了,造成了该属性如果是数据描述则优先访问的假象
  说到底,属性访问顺序就从来没有变过
  一句话总结:非数据描述器可以覆盖,数据描述器直接修改类
  在py中,所有的方法都是数据描述器
  实现一个static装饰器
  静态方法的本质
  全局函数放到类中,使用时候,通过我们的类对象进行使用
class A:  
    @staticmethod
  
    def bar():
  
      return 1
  
    def test(self):
  
      return 2
  
f = A()
  
print(f.test)
  
print(f.bar)
  查看结果
<bound method A.test of <__main__.A object at 0x0000000000D86278>>  
<function A.bar at 0x0000000000DF11E0>
  静态方法是作为一个function传递进来的
  首先我们搞明白需求 如何调用的 A.foo 这么调用
  基础框架
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的实例
a = A.foo  
print(a)
  
None
  相当于在定义foo的时候被传递给StaticMethod(foo)
  当前的foo相当于一个实例对象
  返回的东西加了括号才可以调用,所以必须返回self
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原封不动的返回,打印他们的内存地址查看
fn: <function A.foo at 0x0000000000DEAA60>  
<__main__.Static_Method object at 0x0000000000A764E0> None <class '__main__.A'>
  
f: <function A.foo at 0x0000000000DEAA60>
http://blog.51cto.com/static/js/ueditor1.4.3/themes/default/images/spacer.gif
  等价式:foo = Static_Method(foo)
  就是说,调用的时候,必须以func类型传递到Statice_Method中
class A:  
# @Static_Met
  
def foo():
  
    print('static')
  
print(A.foo)
  返回为:
  <function A.foo at 0x0000000000E3F9D8>
页: [1]
查看完整版本: python魔术方法之装饰器