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
    以上代码执行结果戳这里
  • 相关阅读:
    关于32位操作系统和64位操作系统对InstallShield打包的影响
    NEWS: Symantec宣布Wise Package Studio将终止
    InstallShield 2012新功能试用(2) 调用MsiGetProperty等MSI API发生变化
    Basic INFO 在命令行Build InstallShield安装包工程获得压缩安装包
    NEWS InstallShield 2012 Service Pack 1发布
    Basic INFO InstallShield Basic MSI工程中如何在SetupCompleteSuccess界面中启动Readme
    Basic INFO InstallShield的脚本编辑器中如何显示代码行号
    Basic INFO 关于在InstallShield制作的安装包界面中删除InstallShield文字的厂商回复
    Basic INFO InstallShield工程中如何让产品的快捷方式名称始终与产品名保持一致
    Basic INFO: 创建隐藏文件夹
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/11279640.html
Copyright © 2011-2022 走看看