zoukankan      html  css  js  c++  java
  • Pthon魔术方法(Magic Methods)-反射

          Pthon魔术方法(Magic Methods)-反射

                               作者:尹正杰

    版权声明:原创作品,谢绝转载!否则将追究法律责任。

    一.反射概述

      运行时,区别于编译时,指的时程序被加载到内存中执行的时候。

      反射,reflection,指的时运行时获取类型定义信息。

      一个对象能够再运行时,像照镜子一样,反射出其类型信息。

      简单的说,再python这种,能够通过一个对象,找出其type,class,attribute或method的能力,称为反射或者自省。

      具有反射能力的函数有type(),isinstance(),callable(),dir(),getattr()等。

    二.反射相关的函数和方法

    1>.看一个简单需求

      有一个Point类,查看它的实例的属性,并修改它。动态为实例增加属性
     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 class Point:
     8     def __init__(self,x,y):
     9         self.x = x
    10         self.y = y
    11 
    12     def __str__(self):
    13         return  "Point({},{})".format(self.x,self.y)
    14 
    15     def show(self):
    16         print(self.x,self.y)
    17 
    18 p = Point(10,20)
    19 print(p)
    20 print(p.__dict__)
    21 p.__dict__['y'] = 30
    22 print(p.__dict__)
    23 p.z = 10
    24 print(p.__dict__)
    25 print(dir(p))
    26 print(p.__dir__())
    Point(10,20)
    {'x': 10, 'y': 20}
    {'x': 10, 'y': 30}
    {'x': 10, 'y': 30, 'z': 10}
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z']
    ['x', 'y', 'z', '__module__', '__init__', '__str__', 'show', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
    以上代码执行结果戳这里

    2>.Python提供了内置的反射函数

    getattr(object,name[,default])
      通过name返回object的属性值。当属性不存在,将使用default返回,如果没有default,则抛出AttributeError。name必须为字符串。

    setattr(object,name,value)
      object的属性存在,则覆盖,不存在,则新增。

    hasattr(object,name)
      判断对象是否有这个名字的属性,name必须为字符串。

    用上面的方法来修改上例的代码,具体代码如下:
     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 class Point:
     8     def __init__(self,x,y):
     9         self.x = x
    10         self.y = y
    11 
    12     def __str__(self):
    13         return  "Point({},{})".format(self.x,self.y)
    14 
    15     __repr__ = __str__
    16 
    17     def show(self):
    18         print(self.x,self.y)
    19 
    20 p1 = Point(10,20)
    21 p2 = Point(50,100)
    22 
    23 print(repr(p1),repr(p2),sep="
    ")
    24 print(p1.__dict__)
    25 setattr(p1,"y",33)
    26 setattr(p1,"z",66)
    27 print(getattr(p1,"__dict__"))
    28 
    29 #动态调用方法
    30 if hasattr(p1,"show"):
    31     getattr(p1,"show")()
    32 
    33 
    34 #动态为类增加方法
    35 if not hasattr(Point,"add"):
    36     setattr(Point,"add",lambda self,other:Point(self.x + other.x,self.y + other.y))
    37 
    38 print(Point.add)
    39 print(p1.add)           #绑定方法
    40 print(p1.add(p2))       #由于是绑定方法,因此只需要给add传递一个参数即可
    41 
    42 
    43 #为实力增加方法
    44 if not hasattr(p1,"sub"):
    45     setattr(p1,"sub",lambda self,other:Point(self.x - other.x,self.y - other.y))
    46 
    47 print(p1.sub)           #未绑定方法
    48 print(p1.sub(p1,p1))
    49 
    50 #观察add咋子在谁里面,sub在谁里面
    51 print(p1.__dict__)
    52 print(Point.__dict__)
    Point(10,20)
    Point(50,100)
    {'x': 10, 'y': 20}
    {'x': 10, 'y': 33, 'z': 66}
    10 33
    <function <lambda> at 0x00000151BA593708>
    <bound method <lambda> of Point(10,33)>
    Point(60,133)
    <function <lambda> at 0x00000151BA5A5438>
    Point(0,0)
    {'x': 10, 'y': 33, 'z': 66, 'sub': <function <lambda> at 0x00000151BA5A5438>}
    {'__module__': '__main__', '__init__': <function Point.__init__ at 0x00000151BA5A5678>, '__str__': <function Point.__str__ at 0x00000151BA5A51F8>, '__repr__': <function Point.__str__ at 0x00000151BA5A51F8>, 'show': <function Point.show at 0x00000151BA5A5318>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None, 'add': <function <lambda> at 0x00000151BA593708>}
    以上代码执行结果戳这里

    3>.这种动态增加属性的方式和装饰器修饰一个类,Mixin方式的差异在哪?

      这种动态增删属性的方式是运行时来改变类或者实例的方式,但是装饰器或Mixin都是定义时就决定了,因此反射能力具有更大的灵活性。

    三.反射相关的魔术方法

    1>.反射相关的魔术方法概述

    __getattr__():
      当通过搜索实例,实例的类及在祖先类查不到属性,就会调用此方法。
    
    __setattr__():
      通过"."访问实例属性,进行增加,修改都要调用它。
    
    __delattr__():
      当通过实例来删除属性时调用此方法。
    
    __getattribute():
      实例所有的属性调用都从这个方法开始。

    属性查找顺序:
      实例调用__getattribute__() ---> instance.__dict__ ---> instance.__class__.__dict__ ---> 继承的祖先类(知道object)的__dict__ ---> 调用__getattr__()

    2>."__getattr__()"方法测试

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 class Base:
     8     m  = 100
     9 
    10 class Point(Base):
    11     n = 200
    12 
    13     def __init__(self,x,y):
    14         self.x = x
    15         self.y = y
    16 
    17     def __str__(self):
    18         return  "Point({},{})".format(self.x,self.y)
    19  
    20     def __getattr__(self, item):
    21         return "missing {}".format(item)
    22 
    23 
    24 p1 = Point(10,20)
    25 print(p1.x)
    26 print(p1.n)
    27 print(p1.m)
    28 print(p1.t)     #实例属性会按照继承关系找,如果找不到,就会执行"__getattr__()"方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性。
    10
    200
    100
    missing t
    以上代码执行结果戳这里

    3>."__setattr__()"方法测试

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 class Base:
     8     m  = 100
     9 
    10 class Point(Base):
    11     n = 200
    12 
    13     def __init__(self,x,y):
    14         self.x = x
    15         self.y = y
    16 
    17     def __str__(self):
    18         return  "Point({},{})".format(self.x,self.y)
    19 
    20     def __getattr__(self, item):
    21         return "missing {}".format(item)
    22 
    23     def __setattr__(self, key, value):
    24         """
    25             实例通过点号(".")设置属性,例如"self.x = x"属性赋值,就会调用"def __setattr__(self, key, value)"方法,该方法可用拦截对实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的"__dict__"。
    26         很显然,我们在"__setattr__"方法中并没有给修改实例的"__dict__",而是仅仅时执行了打印语句,因此实例的"__dict__"中默认不会存在任何属性。
    27         """
    28         print("setattr {} = {}".format(key,value))
    29 
    30 
    31 p1 = Point(10,20)       #实例化过程中会调用"__init__"方法,而"__init__"方法存在"self.x = x"的语句,因此每当执行类似语句都会调用"__setattr__"方法
    32 print(p1.x)             #如上所述,"def __setattr__(self, key, value)"并没有将"x"属性加入到实例字典中,且p1实例的类和父类Base以及Object类都没有"x"属性,因此在这里会调用"__getattr__"方法。
    33 print(p1.n)
    34 print(p1.m)
    35 print(p1.t)             #实例属性会按照继承关系找,如果找不到,就会执行"__getattr__()"方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性。
    36 
    37 p1.x = 50
    38 print(p1.x)
    39 print(p1.__dict__)
    40 p1.__dict__["x"] = 100  #实例属性要加到实例的"__dict__"中才能访问到,既然"def __setattr__(self, key, value)"没有帮我们完成这样的操作,我们就需要自己完成
    41 print(p1.__dict__)
    42 print(p1.x)             #由于将"x"属性加入到实例字典中,因此我们可用访问到"x"属性啦!
    setattr x = 10
    setattr y = 20
    missing x
    200
    100
    missing t
    setattr x = 50
    missing x
    {}
    {'x': 100}
    100
    以上代码执行结果戳这里

    4>."__delattr__()"方法测试

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 class Base:
     8     m  = 100
     9 
    10 class Point(Base):
    11     n = 200
    12     d = {}
    13     def __init__(self,x,y):
    14         self.x = x
    15         setattr(self,"y",y)
    16         self.__dict__["a"] = 50
    17 
    18     def __str__(self):
    19         return  "Point({},{})".format(self.x,self.y)
    20 
    21     def __getattr__(self, item):
    22         print( "missing {}".format(item))
    23         return self.d[item]
    24 
    25     def __setattr__(self, key, value):
    26         print("setattr {} = {}".format(key,value))
    27         self.d[key] = value
    28 
    29     def __delattr__(self, item):
    30         """
    31             可用阻止通过实例来删除属性的操作。
    32         """
    33         print("Can not del {}".format(item))
    34 
    35     def show(self):
    36         print(self.x,self.y)
    37 
    38 
    39 p1 = Point(10,20)
    40 p1.show()
    41 print(p1.a)
    42 print(p1.__dict__)
    43 print(Point.__dict__)  
    44 del p1.x
    45 p1.q = 15
    46 print(p1.__dict__)
    47 del p1.q
    48 del p1.n
    49 del Point.n
    50 print(Point.__dict__)       #可用阻止通过实例来删除属性的操作,但是通过类依然可以删除
    setattr x = 10
    setattr y = 20
    missing x
    missing y
    10 20
    50
    {'a': 50}
    {'__module__': '__main__', 'n': 200, 'd': {'x': 10, 'y': 20}, '__init__': <function Point.__init__ at 0x00000276AEF35318>, '__str__': <function Point.__str__ at 0x00000276AEF35438>, '__getattr__': <function Point.__getattr__ at 0x00000276AEF354C8>, '__setattr__': <function Point.__setattr__ at 0x00000276AEF35558>, '__delattr__': <function Point.__delattr__ at 0x00000276AEF358B8>, 'show': <function Point.show at 0x00000276AEF35948>, '__doc__': None}
    Can not del x
    setattr q = 15
    {'a': 50}
    Can not del q
    Can not del n
    {'__module__': '__main__', 'd': {'x': 10, 'y': 20, 'q': 15}, '__init__': <function Point.__init__ at 0x00000276AEF35318>, '__str__': <function Point.__str__ at 0x00000276AEF35438>, '__getattr__': <function Point.__getattr__ at 0x00000276AEF354C8>, '__setattr__': <function Point.__setattr__ at 0x00000276AEF35558>, '__delattr__': <function Point.__delattr__ at 0x00000276AEF358B8>, 'show': <function Point.show at 0x00000276AEF35948>, '__doc__': None}
    以上代码执行结果戳这里

    5>."__getattribute__()"方法测试

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 class Base:
     8     m  = 100
     9 
    10 class Point(Base):
    11     n = 200
    12     def __init__(self,x,y):
    13         self.x = x
    14         setattr(self,"y",y)
    15 
    16     def __getattr__(self, item):
    17         print( "missing {}".format(item))
    18         return self.d[item]
    19 
    20     def __getattribute__(self, item):
    21         """
    22         实例的所有的属性访问,第一个都会调用"def __getattribute__(self, item)"方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常。
    23             它的return值将作为属性查找的结果
    24             如果抛出AttributeError异常,则会直接调用"__getattr__"方法,因为表述属性没有找到。
    25 
    26         温馨提示:
    27             "__getattribute__"方法中为了避免在该方法中无线的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如"object.__getattribute__(self,name)"。
    28             注意,除非你明确地知道"__getattribute__"方法用来做什么,否则不要使用它。
    29         """
    30         return item
    31 
    32 
    33 p1 = Point(10,20)
    34 print(p1.__dict__)
    35 print(p1.x)
    36 print(p1.n)
    37 print(p1.n)
    38 print(p1.t)
    39 print(Point.__dict__)
    40 print(Point.n)
    __dict__
    x
    n
    n
    t
    {'__module__': '__main__', 'n': 200, '__init__': <function Point.__init__ at 0x00000230DA543708>, '__getattr__': <function Point.__getattr__ at 0x00000230DA555678>, '__getattribute__': <function Point.__getattribute__ at 0x00000230DA5551F8>, '__doc__': None}
    200
    以上代码执行结果戳这里
  • 相关阅读:
    Java再学习——栈(stack)和堆(heap)
    Java再学习——深究static关键字
    毕业设计进度:3月4日
    毕业设计进度:3月3日
    可视化组件:echarts柱状图添加点击事件
    毕业设计进度:2月29日
    毕业设计进度:2月28日
    毕业设计进度:2月27日
    毕业设计进度:2月26日
    毕业设计进度:2月25日
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/11279640.html
Copyright © 2011-2022 走看看