管理属性有四种方式:
1、__getattr__和__setattr__:把未定义的属性获取和所有的属性赋值指向通用的处理器方法。
2、__getattribute__:把所有的属性获取和赋值指向Python2.6中的新式类和Python 3.0中的所有类的中的一个处理器方法
3、property内置函数,把特定属性访问定位到get和set函数,也叫做特性
4、描述符:把特定属性访问定位到具有任意get和set函数的类的实例
特性
attribute = property(fget, fset, fdel, doc)
通过property把一个内置函数的结果赋给一个类属性。向 fget 传递一个函数来拦截属性访问,给 fset 传递一个函数进行赋值,并且给 fdel传递一个函数进行属性删除; doc参数接收该属性的一个文档字符串。fget有返回值,而fset和fdel没有返回值。
class Person(object): def __init__(self, name): self._name = name def getName(self): print 'fetch...' return self._name def setName(self, value): print 'change...' self._name = value def delName(self): print 'remove...' del self._name name = property(getName, setName, delName, "name property docs") 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__
使用装饰器编写特性
class Person(object): def __init__(self, name): self._name = name @property def name(self): #name=property(name) "name property docs" print 'fetch...' return self._name @name.setter def name(self, value): #name=name.setter(name) print 'change...' self._name = value @name.deleter #name=name.deleter(name) def name(self): print 'remove...' del self._name 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__
property对象也有getter、setter和deleter方法,这些方法指定相应的特性访问器方法赋值并且返回特性自身的一个副本。我们也可以使用这些方法,通过装饰常规方法来指定特性的组成部分。 getattr 部分通常由创建特性自身的行为自动填充。
描述符
从功能上讲,描述符协议允许我们把一个特定属性的get和set操作指向我们提供的一个单独类对象的方法:它们提供了一种方式来插入在访问属性的时候自动运行的代码,并且它们允许我们拦截属性删除并且为属性提供文档。描述符作为独立的类创建,并且它们就像方法函数一样分配给类属性。
class Descriptor(object): "docstring goes here" def __get__(self, instance, owner): ... def __set__(self, instance, value): ... def __delete__(self, instance): ...
带有任何这些方法的类都可以看作是描述符,并且当它们的一个实例分配给另一个类的属性的时候,它们的这些方法是特殊的――当访问属性的时候,会自动调用它们。三个方法都传递了描述符类实例( s e l f )以及描述符实例所附加的客户类的实例( instance )。__get__还传递了用于表示类的owner。instance 参数要么是访问的属性所属的实例(用于instance .attr ),要么当所访问的属性直接属于类的时候是 N o n e (用于c l a s s.a t t r )。
class DescStat(object): def __init__(self,value): self.value=value def __get__(self,instance,owner): return self.value def __set__(self,instance,value): self.value=value def __delete__(self,instance): def self.value class InstState(object): def __get__(self, instance, owner): print 'InstState get' return instance._Y * 100 def __set__(self, instance, value): print 'InstState set' instance._Y = value class CalcAttrs(object): X = DescState(2) Y = InstState() def __init__(self): self._Y = 3 self.Z = 4 a=CalcAttrs()
a是类CalcAttrs的实例。类CalcAttrs含有类属性X,Y,实例属性_Y,Z。X是类DescStat()的实例,类DescStat含有实例属性value。Y是类InstState的实例。InstState无自身属性。对实例a的X属性的访问、赋值会触发DescStat的__get__,__set__方法。对Y属性的相关操作会触发相应的方法,作用于实例属性_Y上。
__getattr__和__getattribute__可以拦截任何实例属性的获取,适合于委托设计模式。
避免陷入潜在的循环(递归)。__getattr__只对未定义的属性调用,可以在代码中自由地获取其他属性。而__getattribute__和__setattr__针对所有的属性,因此它们的代码在访问其他属性的时候应避免再次调用自身并触发一次递归循环。例如,在一个 __g e t a t t r i b u t e__ 方法代码内部的另一次属性获取,将会再次触发__getattribute__ ,并且代码将会循环直到内存耗尽。要解决这个问题,把获取指向一个更高的超类,而不是跳过这个层级的版本―― object类总是一个超类,并且它在这里可以很好地起作用。对于__setattr__也是如此,在一个__setattr__调用中再此进行任何属性赋值。要解决此问题,可以把属性作为实例的__dict__命名空间字典中的一个键赋值。
class Person(object): """docstring for 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') print bob.name bob.name='Jack' print bob.name del bob.name