Bruce 知道,我們需要一種方法來區分對實例變量的引用和對其它變量的引用,因此他建議將“self”設為關鍵字。
考慮一種典型的類,它有一個方法,例如:
class C: def meth(self, arg): self.val = arg return self.val
跟據 Bruce 的提議,這將變為:
class C: def meth(arg):# Look ma, no self! self.val = arg return self.val
這樣每個方法會節省 6 個字符。但我不覺得 Bruce 提出這個建議是為了減少打字。
我認為他真正關心的是程序員(可能來自其它語言)所浪費的時間,有時候似乎不需要指定“self”參數,而且他們偶爾忘記了要加(即使他們十分清楚——習慣是一種強大的力量)。確實,與忘記在實例變量或方法引用之前鍵入“self.”相比,從參數列表中省略“self”,往往會導致很模糊的錯誤消息。
也許更糟糕的是(如 Bruce 所述),當正確地聲明了方法,但是在調用時的參數數量不對,這時收到的錯誤消息。如 Bruce 給出的以下示例:
Traceback (most recent call last):File "classes.py", line 9, in obj.m2(1)TypeError: m2() takes exactly 3 arguments (2 given)
我贊同它是令人困惑的,但是我寧願去解決此錯誤消息,而不是修改語言。
為什麼 Bruce 的提議不可行首先,讓我提出一些與 Bruce 的提議相反的典型論點。
這有一個很好的論據可以證明,在參數列表中使用顯式的“self”,可以增強以下兩種調用方法在理論上的等效性。假設“ foo”是“C”的一個實例:
foo.meth(arg) == C.meth(foo, arg)
(譯注:說實話,我沒有理解這個例子的意思。以下僅是個人看法。在類的內部定義方法時,可能會產生幾種不同的方法:實例方法、類方法和 靜態方法。它們的作用和行為是不同的,那麼在定義和調用時怎麼做區分呢?Python 約定了一種方式,即在定義時用第一個參數作區分:self 表示實例方法、cls或其它符號 表示類方法……三種方法都可以被類的實例調用,而且看起來一模一樣,如上例的等號左側那樣。這時候就要靠定義時賦予的參數來區分了,像上例等號右側,第一個參數是實例對象,表明此處是個實例方法。)
另一個論據是,在參數列表中使用顯式的“self”,將一個函數插入一個類,獲得動態地修改一個類的能力,創建出相應的一個類方法。
例如,我們可以創建一個與上面的“C”完全等效的類,如下所示:
# Define an empty class:class C: pass# Define a global function:def meth(myself, arg): myself.val = arg return myself.val# Poke the method into the class:C.meth = meth
請注意,我將“self”參數重命名為“myself”,以強調(在語法上)我們不是在此處定義一個方法(譯注:類外部的是函數,即 function,類內部的是方法,即 method)。
這樣之後,C 的實例就具有了一個“meth”方法,該方法有一個參數,且功能跟之前的完全一樣。對於在把方法插入類之前就創建的那些 C 的實例,它甚至也適用。
我想 Bruce 並不特別在意前述的等效性。我同意這只是理論上的重要。我能想到的唯一例外是舊式的調用超級方法的習語(idiom)。但是,這個習語很容易出錯(正是由於需要顯式地傳遞"self"的原因),這就是為什麼在 Python 3000中,我建議在所有情況下都使用"super()"的原因。
Bruce 可能會想到一種使第二個等效例子起作用的方法——在某些情況下,這種等效性真的很重要。我不知道 Bruce 花了多少時間思考如何實現他的提議,但是我想他正在考慮將一個名為“self”的額外形參自動地添加到直接地在類內部定義的所有方法的思路(我必須說是“直接地”,以便那些嵌套在方法內部的函數,能免於這種自動操作)。這樣,可以使第一個等效例子保持等效。
但是,有一種情況我認為 Bruce 不能在不向編譯器中添加某種 ESP 的情況下解決:裝飾器。我相信這是 Bruce 的提議的最終敗筆。
當裝飾一個方法時,我們不知道是否要自動地給它加一個“self”參數:裝飾器可以將函數變成一個靜態方法(沒有“self”)或一個類方法(有一個有趣的 self,它指向一個類而不是一個實例),或者可以做一些完全不同的事情(用純 Python 實現“ @classmethod”或“ @staticmethod”的裝飾器是繁瑣的)。除非知道裝飾器的用途,否則沒有其它辦法來確定是否要賦予正在定義的方法一個隱式的“self”參數。
我拒絕諸如特殊包裝的“@classmethod”和“@staticmethod”之類的黑科技。我也認為除了自檢外,自動地確定某個方法是類方法(class method)、實例方法(instance method)還是靜態方法(static method),這不是一個好主意(就像在 Bruce 的文章的評論中,有人建議的那樣):這使得很難僅僅根據方法前的“def”,來決定應該怎樣調用該方法。
(譯注:對於一個方法,在當前的添加了相應參數的情況下,可以簡單地加裝飾器,區分它是哪種方法,調用時也容易區分調用;但是,如果沒有加參數,即使可以用神奇的自動機制來區分出它是哪種方法,但在調用時,你不好確定該怎麼調用)。
在評論中,我看到了一些非常極端的對 Bruce 的提議的附和,但通常的代價是使得規則難以遵循,或者要求對語言進行更深層的修改,這令我們極其難以接受它,特別是合入 Python 3.1。順便說一句,對於 3.1,再次聲明我們的規則,新特性只有在保持向後兼容的情況下才是可接受的。
有一個似乎可行的建議(可以使它向後兼容)是把類中的
def foo(self, arg): ...
改成這樣的語法糖:
def self.foo(arg): ...
但我不認同它把“self”變為保留字(reserved word),或者要求前綴必須是“self”。如果這樣做了,那對於類方法,很容易也出現這種情況:
@classmethoddef cls.foo(arg): ...
好了,相比於現狀,我並沒有更喜歡這個。但是相比於 Bruce 的提議或在他的博客評論區中提出的更極端的說法,我認為這個要好得多,而且它具有向後兼容的巨大優勢,並且不需要很費力,就可以寫成帶有參考實現的 PEP。(我想 Bruce 應該會發現自己提案中的缺陷,如果他真的付出努力嘗試編寫可靠的 PEP 或者嘗試實現它。)
到此這篇關於Python為什麼要保留顯式的self的文章就介紹到這了,更多相關Python保留顯式self內容請搜索軟件開發網以前的文章或繼續浏覽下面的相關文章希望大家以後多多支持軟件開發網!