>>> Temp.test = test
>>> t.test(1)
<__main__.Temp object at 0x00B46E90> 1
>>> t.test = test
>>> t.test(1)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: test() takes exactly 2 arguments (1 given)
>>> t.test
<function at="" test="">
</function></stdin>
咦?不是说 function 是 descriptor 的吗?怎么这里没有去调用它的 __get__ 方法呢?
另外:
>>> class Meta(type):pass
...
>>> class Temp(object):
... __metaclass__ = Meta
...
>>> class Desc(object):
... def __get__(self, instance, type):
... print instance, type
...
>>> desc = Desc()
>>> Meta.d = desc
>>> Meta.d
None <class __main__.meta="">
>>> Temp.d
<class __main__.temp=""> <class __main__.meta="">
>>> Temp.d = desc
>>> Temp.d
None <class __main__.temp="">
>>> t = Temp()
>>> t.d
<__main__.Temp object at 0x00B46DD0> <class __main__.temp="">
>>> t.d = desc
>>> t.d
<__main__.Desc object at 0x00B46D30>
</class></class></class></class></class>
注意到,到最后一步 t.d 的时候也没有对 descriptor 求值。这个道理和上面那个是一样的,仔细看一下 attribute 查找策略 就可以找到答案了, descriptor 只有绑定在 type object 上才有效。
这里我们涉及到了 python对象一种分类: type object 和 非 type object ,这两种对象在 attribute 查找过程中的待遇是不一样的。
简单地说 type object 包括 type, type 的子类( 也就是 metaclass 了 )、 type 的实例( 也就是 class 了 )
一般来说 type object 和 非 type object 不光在 attribute 受到不平等待遇,而且非 type object 还不能成为其它对象的基类型,想成为 metaclass 更是痴心妄想了。
不过就像我以前说过的那样,python 中的对象本质上都是平等的,区分它们的唯一方法是它们的接口,所以我相信所谓 type object 与 非 type object 的区别也只在于接口而已。也就是说只要实现 type object 所需的接口,任何对象都可以成为 type object 。
参考:
How-To Guide for Descriptors
Python Attributes and Methods