海上月明

editer by sun
posts - 162, comments - 51, trackbacks - 0, articles - 8
   :: 首页 :: 新随笔 ::  :: 聚合  :: 管理

[转] 理解python中的method与function

Posted on 2008-10-15 14:21 pts 阅读(3710) 评论(0)  编辑  收藏 所属分类: Python
转自:白菜: python

总是看到有人对 python 中的 method 和 function 之间关系的困惑,其实初学 python 时我也困惑过,不过现在自认为对这个问题还是基本清楚了 ;-)。

我在前面写过的 selfless python 里面说过 method 本质上就是 function,这个从它们的形式上也看得出来,呵呵,而让人困惑的问题主要就是那个隐式传入的 self 参数。这其实是利用了descriptor 机制,请看代码:

/>>> class Temp(object):
... def test(self, a):
... print self, a
...
/>>> func = Temp.__dict__['test']
/>>> func

/>>> func(1, 2)
1 2

由此可见 test 就是个不折不扣的函数!

/>>> Temp.test

/>>> t = Temp()
/>>> t.test
<__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 "", line 1, in ?
TypeError: test() takes exactly 2 arguments (1 given)
/>>> t.test


咦?不是说 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

/>>> Temp.d

/>>> Temp.d = desc
/>>> Temp.d
None
/>>> t = Temp()
/>>> t.d
<__main__.Temp object at 0x00B46DD0>

/>>> 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 。

参考:

How-To Guide for Descriptors
Python Attributes and Methods


只有注册用户登录后才能发表评论。


网站导航: