描述符
很少有人会去刻意关注描述符 (Descriptor),尽管它时时刻刻以属性、方法的身份出现。
描述符协议:
__get__(self, instance, owner) --> return value
__set__(self, instance, value)
__delete__(self, instance)
描述符对象以类型 (owner class) 成员的方式出现,且最少要实现一个协议方法。最常见的描述符有 property、staticmethod、classsmethod。访问描述符类型成员时,解释器会自动调用与行为相对应的协议方法。
- 实现 get 和 set 方法,称为 data descriptor。
- 仅有 get 方法的,称为 non-data descriptor。
- get 对 owner_class、owner_instance 访问有效。
- set、delete 仅对 owner_instance 访问有效。
>>> class MyDescriptor(object):
... def __get__(self, instance, owner): # 本例中 owner 是 class Data。
... print "get:", instance, owner
... return hex(id(instance))
...
... def __set__(self, instance, value):
... print "set:", instance, value
...
... def __delete__(self, instance):
... print "del:", instance
>>> class Data(object):
... x = MyDescriptor()
>>> d = Data()
>>> d.x # __get__ 的返回值。
get: <__main__.Data object at 0x107a23790> <class '__main__.Data'>
'0x107a23790'
>>> d.x = 100 # d 被当做 instance 实参。
set: <__main__.Data object at 0x107a23790> 100
>>> del d.x # d 被当做 instance 实参。
del: <__main__.Data object at 0x107a23790>
>>> Data.x # 以 owner 类型访问时,__get__ 有效。
get: None <class '__main__.Data'> # instance = None
'0x106a96148'
>>> Data.x = 1 # __set__ 对 class 调用无效。
# 因此 Data.x 被重新赋值。
>>> type(Data.x)
<type 'int'>
如果没有定义 get 方法,那么直接返回描述符对象,不会有默认 get 实现。
property
属性总是 data descriptor,这和是否提供 setter 无关。其优先级总是高过同名实例字段,如果没有提供 setter,set 方法会阻止赋值操作。
>>> class Data(object):
... oid = property(lambda s: hex(id(s)))
>>> hasattr(Data.oid, "__set__")
True
>>> d = Data()
>>> d.oid
'0x107a23a90'
>>> d.oid = 123
AttributeError: can't set attribute
non-data
non-data descriptor 会被同名实例字段抢先。
>>> class Descriptor(object):
... def __get__(self, instance, owner):
... print "__get__"
>>> class Data(object):
... x = Descriptor()
>>> d = Data()
>>> d.x # 描述符有效。
__get__
>>> d.__dict__ # instance.__dict__ 没有同名字段。
{}
>>> d.x = 123 # 没有 __set__,创建同名实例字段。
>>> d.__dict__
{'x': 123}
>>> d.x # 依据成员查找规则,实例字段被优先命中。
123
>>> Data.x # 描述符在 owner_class.__dict___。
__get__
bound method
通过描述符,我们可以了解实例方法 self 参数是如何隐式传递的。
>>> class Data(object):
... def test(self): print "test"
>>> d = Data()
>>> d.test # 只有 bound method 才会隐式传递 self。
<bound method Data.test of <__main__.Data object at 0x10740b050>>
>>> Data.test.__get__(d, Data) # 向 __get__ 传递 instance 参数。
<bound method Data.test of <__main__.Data object at 0x10740b050>>
>>> Data.test # unbound method 需显式传递 self。
<unbound method Data.test>
>>> Data.test.__get__(None, Data) # instance 为 None。
<unbound method Data.test>
现在可以看出,bound/unbound 是 get 造成的,关键就是 instance 参数。那么 self 参数存在哪?由谁替我们自动传递 self 参数呢?
>>> bm = Data.test.__get__(d, Data)
>>> bm.__func__ # 实际的目标函数 test。
<function test at 0x107404488>
>>> bm.__self__ # __get__ instance 参数,也就是 self。
<__main__.Data object at 0x10740b050>
>>> bm.__call__() # __call__ 内部替我们传递 self !
test
>>> unbm = Data.test.__get__(None, Data) # unbound method
>>> unbm.__func__
<function test at 0x107404488>
>>> unbm.__self__ is None # instance == None, self == None。
True
>>> unbm.__call__() # __call__ 会检查 __self__。
TypeError: unbound method test() must be called with Data instance as first argument
(got nothing instead)
>>> unbm.__call__(d) # 只好给 __call__ 有效的 instance。
test
classmethod
不同于 staticmethod,classmethod 会 bound 类型对象。
>>> class Data(object):
... @classmethod
... def test(cls): print cls
>>> Data.test.__get__(None, Data)
<bound method type.test of <class '__main__.Data'>>
>>> m = Data.test.__get__(None, Data)
>>> m.__self__ # 类型对象,也就是隐式 cls 参数。
<class '__main__.Data'>
>>> m.__call__()
<class '__main__.Data'>