簡體   English   中英

指向Python中的靜態方法

[英]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 ),並且該值是一個函數時,那么該類將該函數“包裝”到一個特殊的“未綁定方法”對象中,因為他們假設作為函數且未使用staticmethodclassmethod修飾的類的屬性是實例方法(在這種情況下是不正確的假設)。 這個未綁定的方法在調用時需要一個參數,即使在這種情況下底層函數不期望參數。

語言參考中解釋了此行為:

(在“課程”部分)

當類屬性引用(對於類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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM