描述器其实是任何新式类(新式类是继承自 type 或者 object 的类),这种类至少实现了3个特殊的方法__get__
, __set__
, __delete__
中的一个。而这3个特殊的方法充当描述器协议的做用。html
同时实现了__get__()
和__get__()
的类被称为数据描述器(data descriptor)。只实现了 __get__()
方法的类是非数据描述器(经常使用于方法,固然其余用途也是能够的)。python
__get__()
,__get__()
和 __delete__
的原型以下:segmentfault
Descriptor.__get__(self, instance, owner) --> value Descriptor.__set__(self, instance, value) --> None Descriptor.__delete__(self, instance) --> None
python的object.__get__(self, instance, owner)
:ide
Called to get the attribute of the owner class (class attribute access) or of an instance of that class函数
(instance attribute access). owner is always the owner class, while instance is the instance that the attribute was accessed through, or None when the attribute is accessed through the owner. This method should return the (computed) attribute value or raise an AttributeError exception.
整个描述器的核心是__getattribute__()
,由于对像任何属性的访问都会调用到这个特殊的方法。这个方法被用来查找属性,同时也是你的一个代理,调用它能够进行属性的访问操做。
通常咱们的类的__getattribute__()
方法都是继承自object
,本身改写__getattribute__()
是很危险的,也会阻止正常的描述器调用。__getattribute__()
的Python描述原型以下:ui
def __getattribute__(self, key): "Emulate type_getattro() in Objects/typeobject.c" v = object.__getattribute__(self, key) if hasattr(v, '__get__'): return v.__get__(None, self) return v
若是经过实例ins
访问描述器,由__getattribute__()
转化为: type(ins).__dict__['attr'].__get__(ins, type(ins)
若是经过类Class访问描述器,由__getattribute__()
转化为:Class.__dict__['attr'].__get__(None, Class)
翻译
class Descriptor(object): def __init__(self): self.aaaa = 'anonymous' def __get__(self, instance, owner): print('instance: %s' % instance) print('owner: %s' % owner) print("Invoke __get__: %s" % self.aaaa) return self.aaaa def __set__(self, instance, name): print("invoke __set__: %s" % name) self.aaaa = name.title() def __delete__(self, instance): print("Invoke __delete__: %s" % self.aaaa) del self.aaaa class Person(object): name = Descriptor() # 经过类Person访问 print(Person.name) # instance: None # owner: <class '__main__.Person'> # Invoke __get__: anonymous # anonymous print(Person.__dict__['name'].__get__(None, Person)) # instance: None # owner: <class '__main__.Person'> # Invoke __get__: anonymous # anonymous user = Person() # 经过实例user访问, `owner`访问描述器实例的对象。`instance`则是访问描述器实例的实例 print(user.name) # instance: <__main__.Person object at 0x7f88c5472dd0> # owner: <class '__main__.Person'> # Invoke __get__: anonymous # anonymous print(type(user).__dict__['name'].__get__(user, type(user))) # instance: <__main__.Person object at 0x7f0873fb5d90> # owner: <class '__main__.Person'> # Invoke __get__: anonymous # anonymous user.name = 'jack' # invoke __set__: jack del user.name # Invoke __delete__: Jack
另外经过super访问,如SubPerson
是Person
的子类,super(SubPerson, subins).name)
访问经过subins.__class__.__mro__
查找到Person
类,而后调用:Person.__dict__['name'].__get__(subins, Person)
。代理
class SubPerson(Person): pass subins = SubPerson() print(subins.__class__.__mro__) # (<class '__main__.SubPerson'>, <class '__main__.Person'>, <class 'object'>) # 经过super访问 print(super(SubPerson, subins).name) # instance: <__main__.SubPerson object at 0x7f30b1537f28> # owner: <class '__main__.Person'> # Invoke __get__: anonymous # anonymous print(Person.__dict__['name'].__get__(subins, Person)) # instance: <__main__.SubPerson object at 0x7f30b1537f28> # owner: <class '__main__.Person'> # Invoke __get__: anonymous # anonymous
class ClassA(object): def __init__(self, classname): self.classname = classname def __getattr__(self, attr): return('invoke __getattr__', attr) def __getattribute__(self, attr): return('invoke __getattribute__', attr) insA = ClassA('ClassA') print(insA.__dict__) # ('invoke __getattribute__', '__dict__') print(insA.classname) # ('invoke __getattribute__', 'classname') print(insA.grade) # ('invoke __getattribute__', 'grade')
上面提到实例ins
访问描述器,实际是由__getattribute__()
访问: type(ins).__dict__['attr'].__get__(ins, type(ins)
。
具体实现是依据这样的优先顺序是:数据描述器 > 实例属性 > 非数据描述符 -> __getter__() 方法
以下,咱们user.name = 'andy'
咱们经过实例对属性name
赋值,但因为数据描述器优先级高于实例属性。赋值操做被数据描器中的__set__
方法截获,咱们在__set__
忽略了从新赋值(固然也能够在其中更新赋值,但实质不是经过实例属性绑定的方式)。易见实例user
的属性字典__dict__
仍是空的。code
class Descriptor(object): def __init__(self, name): self.aaaa = name def __get__(self, instance, owner): print("Invoke __get__: %s" % self.aaaa) return self.aaaa def __set__(self, instance, name): print("invoke __set__, ignore assignment.") def __delete__(self, instance): print("Invoke __delete__: %s" % self.aaaa) del self.aaaa class Person(object): name = Descriptor('jack') user = Person() print(user.name) # Invoke __get__: jack # jack print(user.__dict__) # {} user.name = 'andy' # 实例属性赋值 # invoke __set__, ignore assignment. print(user.name) # Invoke __get__: jack # jack print(user.__dict__) # {}
再看非数据描述器和实例属性比较。user.name = 'andy'
成功的把属性name
绑定到user.__dict__
中。htm
class Descriptor(object): def __init__(self, name): self.aaaa = name def __get__(self, instance, owner): print("Invoke __get__: %s" % self.aaaa) return self.aaaa class Person(object): name = Descriptor('jack') user = Person() print(user.name) # Invoke __get__: jack # jack print(user.__dict__) # {} user.name = 'andy' print(user.name) # andy print(user.__dict__) # {'name': 'andy'}
若是经过类Class访问描述器,由__getattribute__()
访问:Class.__dict__['attr'].__get__(None, Class)
。
优先级是:类属性 > 描述器。
经过类对象Person.name = 'andy'
更新属性name
,并无进入到描述器的__set__
方法中,并且Person.__dict__
中的属性name
也由描述器<__main__.Descriptor object at 0x7f1a72df9710>
更新为字符串'andy'
。可见类属性的优先级高于描述器。
class Descriptor(object): def __init__(self, name): self.aaaa = name def __get__(self, instance, owner): print("Invoke __get__: %s" % self.aaaa) return self.aaaa def __set__(self, instance, name): print("invoke __set__, ignore assignment.") def __delete__(self, instance): print("Invoke __delete__: %s" % self.aaaa) del self.aaaa class Person(object): name = Descriptor('jack') print(Person.__dict__) # {'__module__': '__main__', 'name': <__main__.Descriptor object at 0x7f1a72df9710>, # '__dict__': <attribute '__dict__' of 'Person' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Person' objects>} # Invoke __get__: jack print(Person.name) # jack Person.name = 'andy' print(Person.__dict__) # {'__module__': '__main__', 'name': 'andy', '__dict__': <attribute '__dict__' of 'Person' objects>, # '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Person' objects>} print(Person.name) # andy
综上,__getattribute__
方法查找属性的优先级是:
类属性 > 数据描述器 > 实例属性 > 非数据描述符 > __getter__() 方法
若是有__getattribute__
方法,当__getattribute__
出现异常时可能会调用__getter__()
。
函数包含一个 __get__()
方法以便在属性访问时绑定方法。这就是说全部的函数都是非资料描述器,它们返回绑定(bound)仍是非绑定(unbound)的方法取决于他们是被实例调用仍是被类调用。用Python代码来描述就是:
class Function(object) def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" return types.MethodType(self, obj, objtype)
class MyClass(): def foo(): return('I am method') ins = MyClass() print(MyClass.__dict__['foo']) # <function MyClass.foo at 0x7fc7cf543a60> print(MyClass.foo) # <function MyClass.foo at 0x7fc7cf543a60> print(ins.foo) # # 从实例来访问,返回bound method # <bound method MyClass.foo of <__main__.MyClass object at 0x7fc7cf552710>>
描述器就是属性访问的代理,经过描述器来访问属性,须要把描述器(实例)做为一个类的属性(做为实例的属性没啥用),经过内部的__get__
,__set__
,__delete__
方法处理对一个属性的操做。
class Descriptor(object): def __init__(self, name): self.aaaa = name def __get__(self, instance, owner): print("Invoke __get__: %s" % self.aaaa) return self.aaaa def __set__(self, instance, name): print("invoke __set__, ignore assignment.") def __delete__(self, instance): print("Invoke __delete__: %s" % self.aaaa) del self.aaaa class Person(object): name = Descriptor('jack') user = Person() user.name = 'andy' # invoke __set__, ignore assignment. print(user.name) # Invoke __get__: jack # jack del user.name # Invoke __delete__: jack
class property(fget=None, fset=None, fdel=None, doc=None)
,fget
是获取属性的函数,fset
是设置属性的函数,fdel
是删除属性的函数,doc
是这个属性的文档字符串。
class C: def __init__(self): self._x = None def getx(self): print('invoke getx') return self._x def setx(self, value): print('invoke setx') self._x = value def delx(self): print('invoke delx') del self._x x = property(getx, setx, delx, "I'm the 'x' property.") ins = C() ins.x = 'property' # invoke setx print(ins.x) # invoke getx # property print(C.x.__doc__) # I'm the 'x' property. del ins.x # invoke delx
这种使用很普遍,在python源码中常常碰见。
class C: def __init__(self): self._x = None @property def x(self): """I'm the 'x' property.""" return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x ins = C() ins.x = 'property' print(ins.x) # property print(C.x.__doc__) # I'm the 'x' property. del ins.x
class Property(object): "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError, "unreadable attribute" return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError, "can't set attribute" self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError, "can't delete attribute" self.fdel(obj) def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__)
非数据描述器 StaticMethod 的 Python版本:
class StaticMethod(object): "Emulate PyStaticMethod_Type() in Objects/funcobject.c" def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): return self.f class E(object): @staticmethod def f(x): print(x) # f = staticmethod(f) E.f(3) # 3 E().f(3) # 3
非数据描述器 ClassMethod 的 Python版本:
class ClassMethod(object): "Emulate PyClassMethod_Type() in Objects/funcobject.c" def __init__(self, f): self.f = f def __get__(self, obj, klass=None): if klass is None: klass = type(obj) def newfunc(*args): return self.f(klass, *args) return newfunc class E(object): def f(klass, x): return klass.__name__, x f = classmethod(f) print(E.f(3)) # ('E', 3) print(E().f(3)) # ('E', 3) print(vars(E)) # {'__module__': '__main__', 'f': <classmethod object at 0x028DAAF0>, # '__dict__': <attribute '__dict__' of 'E' objects>, '__weakref__': # <attribute '__weakref__' of 'E' objects>, '__doc__': None} print(vars(E())) # {}