[英]Pointers to static methods in Python
為什么在下面的代碼中,使用類變量作為方法指針會導致未綁定的方法錯誤,而使用普通變量可以正常工作:
class Cmd:
cmd = None
@staticmethod
def cmdOne():
print 'cmd one'
@staticmethod
def cmdTwo():
print 'cmd two'
def main():
cmd = Cmd.cmdOne
cmd() # works fine
Cmd.cmd = Cmd.cmdOne
Cmd.cmd() # unbound error !!
if __name__=="__main__":
main()
完整的錯誤:
TypeError: unbound method cmdOne() must be called with Cmd instance as
first argument (got nothing instead)
您需要使用staticmethod()
來轉換函數:
Cmd.cmd = staticmethod(Cmd.cmdOne)
您正在遇到Python 2.x中“未綁定方法”的行為。 基本上,在Python 2.x中,當你獲得一個類的屬性(例如在這種情況下是Cmd.cmd
),並且該值是一個函數時,那么該類將該函數“包裝”到一個特殊的“未綁定方法”對象中,因為他們假設作為函數且未使用staticmethod
或classmethod
修飾的類的屬性是實例方法(在這種情況下是不正確的假設)。 這個未綁定的方法在調用時需要一個參數,即使在這種情況下底層函數不期望參數。
語言參考中解釋了此行為:
(在“課程”部分)
當類屬性引用(對於類C,比如說)將產生用戶定義的函數對象或[...]時,它將轉換為未綁定的用戶定義的方法對象,其im_class屬性為C.
(在“用戶定義的方法”部分中)
通過從類中檢索用戶定義的函數對象來創建用戶定義的方法對象時,其im_self屬性為None,並且方法對象被稱為未綁定。
[...]
當調用未綁定的用戶定義方法對象時,將調用基礎函數(im_func),並限制第一個參數必須是正確類(im_class)或其派生類的實例。
這就是造成你所看到錯誤的原因。
您可以從方法對象中顯式檢索底層函數並調用它(但顯然不需要這樣做):
Cmd.cmd.im_func()
請注意,Python 3.x 擺脫了未綁定的方法 ,您的代碼在Python 3.x上運行良好
我喜歡從“自下而上”查看此行為。
Python中的函數充當“ 描述符對象 ”。 因此,它有一個__get__()
方法。
對具有此類__get__()
方法的類屬性的讀訪問權被“重定向”到此方法。 對類的屬性訪問以attribute.__get__(None, containing_class)
,而對實例的屬性訪問則映射到attribute.__get__(instance, containing_class)
。
函數的__get__()
方法的任務是將函數包裝在一個包裝self
參數的方法對象中 - 對於屬性訪問實例的情況。 這稱為綁定方法。
在2.x上的類屬性訪問中,函數的__get__()
返回一個未綁定的方法包裝器,而正如我今天 __get__()
,在3.x上,它返回自身。 (注意, __get__()
機制仍然存在於3.x中,但函數只返回它自己。)這幾乎是相同的,如果你看它是如何被調用的,但是一個未綁定的方法包裝器另外檢查正確的類型self
論證。
staticmethod()
調用只是創建一個對象,其__get__()
調用旨在返回最初給定的對象,以便撤消所描述的行為。 這就是HYRY的技巧如何工作:屬性訪問撤消staticmethod()
包裝,調用再次執行,以便“new”屬性具有與舊的屬性相同的狀態,盡管在這種情況下, staticmethod()
似乎應用了兩次(但實際上並非如此)。
(順便說一下:它甚至可以在這個奇怪的環境中運行:
s = staticmethod(8)
t = s.__get__(None, 2) # gives 8
雖然8
不是函數而2
不是類。)
在您的問題中,您有兩種情況:
cmd = Cmd.cmdOne
cmd() # works fine
訪問該類並詢問其cmdOne
屬性staticmethod()
對象。 這是通過__get__()
查詢並返回原始函數,然后調用該函數。 這就是它工作正常的原因。
Cmd.cmd = Cmd.cmdOne
Cmd.cmd() # unbound error
做同樣的Cmd.cmd
,但然后將此函數分配給Cmd.cmd
。 下一行是屬性訪問 - 它再次對函數本身進行__get__()
調用,從而返回一個未綁定的方法,必須使用正確的self
對象作為第一個參數進行調用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.