标签:
__getattr__和__getattribute__与特性和描述符不同,这些方法是Python【操作符重载】协议的一部分——是类的特殊命名的方法,由子类继承。__getattr__和__getattribute__方法也比特性和描述符通用,用于拦截几乎所有的实例属性的获取,而不仅仅是特定名称。因此,这两个方法适合于通用的基于委托的编码方式。
----------------------------------------------------------------------------------------------------------------------------------------
基础知识
如果一个类定义了或继承了如下方法,那么当一个实例用于后面的注释所提到的情况时,它们将自动运行:
def __getattr__(self,name): # 引用实例未定义的属性obj.name def __getattribute(self,name): # 引用所有属性obj.name def __setattr__(self,name,value): # 设置任何属性obj.name = value def __delattr__(self,name,value): # 删除任何属性del obj.name所有这些中,self通常是主体实例对象,name是将要访问的属性的字符串名,value是将要赋给该属性的对象。两个get方法通常返回一个属性的值,另两个方法返回None。例如,要捕获每个属性的获取,我们可以使用上面的两个方法,要捕获属性赋值,可以使用第三个方法:
>>> class Catcher: def __getattr__(self,name): print('Get:',name) def __setattr__(self,name,value): print('Set:',name,value) >>> X = Catcher() >>> X.job Get: job >>> X.pay Get: pay >>> X.pay = 99 Set: pay 99----------------------------------------------------------------------------------------------------------------------------------------
避免属性拦截方法中的循环
使用这些方法要避免潜在的递归循环。
例如,在一个__getattribute__方法代码内部的另一次属性获取,将会再次触发__getattribute__,并且代码将会循环知道内存耗尽:
def __getattribute__(self,name): x = self.other要解决这个问题,把获取指向一个更高的超类,而不是跳过这个层级的版本——object类总是一个超类,并且它在这里可以很好的起作用:
def __getattribute__(self,name): x = object.__getattribute__(self,'other')对于__setattr__,情况是类似的,在这个方法内赋值任何属性,都会再次触发__setattr__并创建一个类似的循环:
def __setattr__(self,name,value): self.other = value要解决这个问题,把属性作为实例的__dict__命名空间字典中的一个键赋值,这样就避免了直接的属性赋值:
def __setattr__(self,name,value): self.__dict__['other'] = value还有一种不常用的方法,__setattr__也可以把自己的属性赋值传递给一个更高的超类而避免循环,就像__getattribute__一样:
def __setattr__(self,name,value): object.__setattr__(self,'other',value)相反,我们不能使用__dict__技巧在__getattribute__中避免循环:
def __getattribute__(self,name): x = self.__dict__['other']因为获取__dict__属性会再次触发__getattribute__,从而导致递归循环。
示例
这里是与特性和描述符一样的示例,不过是用属性操作符重载方法实现的。
class Person: def __init__(self,name): self._name = name def __getattr__(self,attr): if attr == 'name': print('fetch...') return self._name else: raise AttributeError(attr) def __setattr__(self,attr,value): if attr == 'name': print('change...') attr = '_name' self.__dict__[attr] = value def __delattr__(self,attr): if attr == 'name': print('remove...') attr = '_name' del self.__dict__[attr] bob = Person('Bob Smith') print(bob.name) bob.name = 'Robert Smith' print(bob.name) del bob.name print('-'*20) sue = Person('Sue Jones') print(sue.name) #print(Person.name.__doc__) #这里没有与特性等同的用法注意,__init__构造函数中的属性赋值也触发了__setattr__,这个方法捕获了每次属性赋值,即便是类自身之中的那些。运行这段代码,会产生同样的输出:
fetch... Bob Smith change... fetch... Robert Smith remove... -------------------- fetch... Sue Jones还要注意,与特性和描述符不同,这里没有为属性直接声明指定的文档。
def __getattribute__(self,attr): if attr == 'name': print('fetch...') attr = '_name' return object.__getattribute__(self,attr)这些例子虽然与特性和描述符编写的代码一致,但是它并没有强调这些工具的用处。由于它们是通用的,所以__getattr__和__getattribute__在基于委托的代码中更为常用。而在只有单个的属性要惯例的情况下,使用特性和描述符更好。
标签:
原文地址:http://blog.csdn.net/gavin_john/article/details/50826931