我在前面写过的 selfless python 里面说过 method 本质上就是 function,这个从它们的形式上也看得出来,呵呵,而让人困惑的问题主要就是那个隐式传入的 self 参数。这其实是利用了descriptor 机制,请看代码:
>>> class Temp(object): ... def test(self, a): ... print self, a ... >>> func = Temp.__dict__['test'] >>> func <function test at 0x00B48170> >>> func(1, 2) 1 2由此可见 test 就是个不折不扣的函数!
>>> Temp.test <unbound method Temp.test> >>> t = Temp() >>> t.test <bound method Temp.test of <__main__.Temp object at 0x00B46CD0>>但是这又是怎么回事了?哪里冒出个 bound/unbound method 来了?
>>> dir(func) ['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__ge tattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__r educe__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_na me']请注意其中的 __get__ 方法,这就是 descriptor 的标志(任何定义了 __get__, __set__, __delete__ 三个方法中的一个或几个的对象都是 descriptor ,这几个方法的意思大家应该能猜到了) 根据对象 attribute 的查找策略,当 t.test 时,首先根据 attribute查找策略找到这个函数对象,然后会发现它有 __get__ 属性,则调用之,并把它的返回值当作该 attribute 的值。
Temp.test 等价于 Temp.__dict__['test'].__get__(None, Temp) t.test 等价于 Temp.__dict__['test'].__get__(t, Temp)
其实你可以把 func.__get__ 的实现想象成下面这个等价物:
>>> class Function(object): ... def __get__(self, obj, objtype=None): ... import types ... return types.MethodType(self, obj, objtype)
到这里事情已经比较清楚了,不过还有一点可能仍然会让你感到困惑:
>>> 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 test at 0x00B42A30>
咦?不是说 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>
注意到,到最后一步 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 。
参考:
2 评论:
正如我前面说的,function 本身还是 descriptor,所以:
Temp.test 等价于 Temp.__dict__['test'].__get__(None, Temp)
t.test 等价于 Temp.__dict__['test'].__get__(t, Temp)
它们返回的 bound/unbound method 都是 types.MethodType 的实例:
>>> def test(self):pass
...
>>> class Temp(object):pass
...
>>> t = Temp()
>>> import types
>>> types.MethodType(test, None, Temp)
<unbound method Temp.test>
>>> types.MethodType(test, t, Temp)
<bound method Temp.test of <__main__.Temp object at 0x00B46C70>>
我怎么不能发评论呢?奇怪。
博主加油,关注你的新文章^_^
发表评论