2012-06-16 64 views
4

我發現下面的示例輕度令人吃驚:Python的方法查找規則

>>> class Foo: 
     def blah(self): 
      pass 


>>> f = Foo() 
>>> def bar(self): 
     pass 

>>> Foo.bar = bar 
>>> f.bar 
<bound method Foo.bar of <__main__.Foo object at 0x02D18FB0>> 

我期望與每個特定實例相關聯的綁定的方法,和在結構上被放置在它。這似乎是合乎邏輯的,綁定的方法將不得不爲每個實例不同,所以它知道在通過該實例的底層函數 - 實際上:

>>> g = Foo() 
>>> g.blah is f.blah 
False 

但我的認識過程顯然是有缺陷,因爲我不希望將一個函數分配給一個類的屬性會將它放入已經被創建的實例中。

所以,我的問題是雙重的 -

  1. 爲什麼分配功能到一個類追溯適用於實例?真正的查找規則和過程是什麼使得這一點成爲現實?
  2. 這是由語言保證的東西,或只是發生的事情?

回答

11

你想打擊你的頭腦,試試這個:

f.blah is f.blah 

這是正確的,實例方法包裝是每次訪問時間不同

實際上,一個實例方法是一個描述符。換句話說,f.blah居然是:

Foo.blah.__get__(f, type(f)) 

方法實際上並沒有存儲在實例;它們存儲在類中,並且動態生成方法包裝以將方法綁定到實例。

+0

啊,對。我沒有意識到/忘記了將實例傳遞給'__get__'的描述符協議。這會教會我閱讀*全部*相關文檔。 :-) – lvc

+0

參考http://wiki.python.org/moin/FromFunctionToMethod瞭解更多信息。 –

7

實例不包含該方法。查詢過程在您訪問foo.bar時動態發生。它會檢查實例是否具有該名稱的屬性。因爲它沒有,所以它看起來在課堂上,因此它找到了當時班級擁有的任何屬性。請注意,這方面的方法並不特別。如果您設置Foo.bar = 2,您將看到相同的效果;之後,foo.bar將評估爲2.

語言的保證是,屬性查找以這種方式進行:首先是實例,如果在實例中未找到屬性,則爲類。 (查找規則與special methods implicitly invoked via operator overloading, etc.不同。)

僅當您直接爲實例指定屬性時,它纔會隱藏類屬性。

>>> foo = Foo() 
>>> foo.bar 
Traceback (most recent call last): 
    File "<pyshell#79>", line 1, in <module> 
    foo.bar 
AttributeError: 'Foo' object has no attribute 'bar' 
>>> foo.bar = 2 
>>> Foo.bar = 88 
>>> foo.bar 
2 

以上所有內容都是與綁定/未綁定方法分開的問題。 Python中的類機制使用descriptor protocol,因此當您訪問foo.bar時,會立即創建一個新的綁定方法實例。這就是爲什麼你在不同的對象上看到不同的綁定方法實例。但請注意,基本上這些綁定方法依賴於您在類中編寫的方法所定義的相同代碼對象:

>>> foo = Foo() 
>>> foo2 = Foo() 
>>> foo.blah.im_func.__code__ is foo2.blah.im_func.__code__ 
True