|
Posted on 2008-10-15 14:21 pts 閱讀(3725) 評論(0) 編輯 收藏 所屬分類: Python
轉(zhuǎn)自: 白菜: python
總是看到有人對 python 中的 method 和 function 之間關(guān)系的困惑,其實初學(xué) python 時我也困惑過,不過現(xiàn)在自認(rèn)為對這個問題還是基本清楚了 ;-)。
我在前面寫過的 selfless python 里面說過 method 本質(zhì)上就是 function,這個從它們的形式上也看得出來,呵呵,而讓人困惑的問題主要就是那個隱式傳入的 self 參數(shù)。這其實是利用了descriptor 機制,請看代碼:
/>>> class Temp(object):
... def test(self, a):
... print self, a
...
/>>> func = Temp.__dict__['test']
/>>> func
/>>> func(1, 2)
1 2
由此可見 test 就是個不折不扣的函數(shù)!
/>>> 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 的標(biāo)志(任何定義了 __get__, __set__, __delete__ 三個方法中的一個或幾個的對象都是 descriptor ,這幾個方法的意思大家應(yīng)該能猜到了)
根據(jù)對象 attribute 的查找策略,當(dāng) t.test 時,首先根據(jù) attribute查找策略找到這個函數(shù)對象,然后會發(fā)現(xiàn)它有 __get__ 屬性,則調(diào)用之,并把它的返回值當(dāng)作該 attribute 的值。
Temp.test 等價于 Temp.__dict__['test'].__get__(None, Temp)
t.test 等價于 Temp.__dict__['test'].__get__(t, Temp)
其實你可以把 func.__get__ 的實現(xiàn)想象成下面這個等價物:
/>>> class Function(object):
... def __get__(self, obj, objtype=None):
... import types
... return types.MethodType(self, obj, objtype)
到這里事情已經(jīng)比較清楚了,不過還有一點可能仍然會讓你感到困惑:
/>>> 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 的嗎?怎么這里沒有去調(diào)用它的 __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 中的對象本質(zhì)上都是平等的,區(qū)分它們的唯一方法是它們的接口,所以我相信所謂 type object 與 非 type object 的區(qū)別也只在于接口而已。也就是說只要實現(xiàn) type object 所需的接口,任何對象都可以成為 type object 。
參考:
How-To Guide for Descriptors
Python Attributes and Methods
|