首先,直觀的解釋:
當你分配到一個類中的函數,它被轉換成一個不受約束的方法。
未綁定的方法是爲每個類的實例創建綁定方法的神奇事物。
綁定方法是一個神奇的東西,它有特殊的self
參數綁定到它。 (這在3.x中略有不同,因爲沒有未綁定的方法;函數本身就是神奇的東西,它爲每個類的實例創建了一個綁定的方法,但是你明確地問到了2.x.)
所以:
>>> class C(object): pass
>>> def foo(self): print(self)
>>> C.foo = foo
>>> c = C()
現在,你可以打電話c.foo()
和self
參數將與c
進行自動填充。但是,如果沒有參數,則不能撥打C.foo()
,如果沒有參數,則可以調用foo()
。
>>> c.foo()
<__main__.C object at 0x108df7e50>
>>> C.foo()
TypeError: unbound method foo() must be called with C instance as first argument (got nothing instead)
>>> foo()
TypeError: foo() takes exactly 1 argument (0 given)
當然沒有什麼從調用C.foo()
或foo()
有一個明確的說法self
阻止你。你通常不會這麼做,但它工作正常:
>>> C.foo(1)
1
>>> foo(1)
1
如果你看一下功能,不受約束的方法,以及綁定的方法,他們很容易分辨:
>>> foo, C.foo, c.foo
(<function __main__.foo>, <unbound method C.foo>, <bound method C.foo of <__main__.C object at 0x108df7e50>>)
>>> type(foo), type(C.foo), type(c.foo)
(function, instancemethod, instancemethod)
如果您只是想要解決方法,可以使用staticmethod
。這需要一個功能,並創建一個事情是,當分配到一類,就像一個函數,而不是一個不受約束的方法:
>>> bar = staticmethod(foo)
>>> C.bar = staticmethod(foo)
>>> c = C()
>>> bar, C.bar, c.bar
(<staticmethod at 0x109e990f8>, <function __main__.foo>, <function __main__.foo>)
而現在,你可以調用它的類或實例作爲一個經常性功能,沒有魔法self
方法:
>>> C.bar(1)
1
>>> c.bar(1)
1
>>> bar(1)
TypeError: 'staticmethod' object is not callable
但是,直觀的解釋是不是真的。它總是會引導你找到正確的答案,但事實並非如此。
要真正理解這一點,您需要了解descriptors的工作方式。
當您將一個函數作爲屬性分配給某個類時,不會發生任何不可思議的事情。什麼都存儲在類字典只是普通的老功能:
>>> C.__dict__['foo']
<function __main__.foo>
而且當你創建一個實例,它不把任何東西的情況下字典,因爲屬性查找自動退回到類詞典:
>>> c.__dict__['foo']
KeyError: 'foo'
真正的魔法發生在查找時間。每當你查找任何屬性(方法或其他)時,解釋器不會從字典中返回對象,它會調用該對象的方法,傳遞實例(如果在類對象上調用,則調用None
)和類。
正常功能有__get__
方法需要產生結合或未結合的方法:
>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
>>> C.__dict__['foo'].__get__(None, C) == C.foo
True
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x108df7e50>>
>>> C.__dict__['foo'].__get__(c, C) == c.foo
True
您可以手動做同樣的事情,並得到相同的結果:
>>> types.MethodType(foo, None, C) == C.foo
True
>>> types.MethodType(foo, c, C) == c.foo
True
,現在你可以大概猜到staticmethod
看起來像什麼:它有一個__get__
方法,它只是返回原始函數,不管你傳遞了什麼。
處理可設置的數據屬性等有一些複雜性,但實際上,這幾乎是整個交易,如果你理解了這麼多,你幾乎可以理解Python中的所有魔法。您可以自行構建staticmethod
,classmethod
和property
,由您自己構建類別等。
感謝攪拌器! –
這是對我的答案中「直觀但不是真正的」部分的更好的解釋(也更簡短!)。而且,即使它不是真的,你可以編寫代碼,就好像它是可行的。 – abarnert