Python 為什麼要保留顯式的 self ?

視學演算法發表於2020-04-06

(給視學演算法加星標,提升Python技能

轉自:豌豆花下貓

布魯斯·埃克爾(Bruce Eckel)發了篇博文[1],提議從類方法的形參列表中刪除“self”。我將解釋為什麼這個提議不能通過。(譯註:Bruce 是《Thinking in Java》、《Thinking in C++》等多本書籍的作者,也是個 Python 開發者。他的文章總結了當年在巴西 Pycon 上的一次討論,主要觀點是在定義類方法時,形參中的“self”是多餘的,而且由它引發的報錯資訊具有一定的誤導性。)

Bruce 的提議

Bruce 知道,我們需要一種方法來區分對例項變數的引用和對其它變數的引用,因此他建議將“self”設為關鍵字。
考慮一種典型的類,它有一個方法,例如:
class C:
   def meth(self, arg):
      self.val = arg
      return self.val
跟據 Bruce 的提議,這將變為:
這樣每個方法會節省 6 個字元。但我不覺得 Bruce 提出這個建議是為了減少打字。
我認為他真正關心的是程式設計師(可能來自其它語言)所浪費的時間,有時候似乎不需要指定“self”引數,而且他們偶爾忘記了要加(即使他們十分清楚——習慣是一種強大的力量)。確實,與忘記在例項變數或方法引用之前鍵入“self.”相比,從引數列表中省略“self”,往往會導致很模糊的錯誤訊息。
也許更糟糕的是(如 Bruce 所述),當正確地宣告瞭方法,但是在呼叫時的引數數量不對,這時收到的錯誤訊息。如 Bruce 給出的以下示例:
我贊同它是令人困惑的,但是我寧願去解決此錯誤訊息,而不是修改語言。

為什麼 Bruce 的提議不可行

首先,讓我提出一些與 Bruce 的提議相反的典型論點。
這有一個很好的論據可以證明,在引數列表中使用顯式的“self”,可以增強以下兩種呼叫方法在理論上的等效性。假設“ foo”是“C”的一個例項:
(譯註:說實話,我沒有理解這個例子的意思。以下僅是個人看法。在類的內部定義方法時,可能會產生幾種不同的方法:例項方法類方法和 靜態方法。它們的作用和行為是不同的,那麼在定義和呼叫時怎麼做區分呢?Python 約定了一種方式,即在定義時用第一個引數作區分:self 表示例項方法、cls或其它符號 表示類方法……三種方法都可以被類的例項呼叫,而且看起來一模一樣,如上例的等號左側那樣。這時候就要靠定義時賦予的引數來區分了,像上例等號右側,第一個引數是例項物件,表明此處是個例項方法。)
另一個論據是,在引數列表中使用顯式的“self”,將一個函式插入一個類,獲得動態地修改一個類的能力,建立出相應的一個類方法。
例如,我們可以建立一個與上面的“C”完全等效的類,如下所示:
請注意,我將“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,再次宣告我們的規則,新特性只有在保持向後相容的情況下才是可接受的。
有一個似乎可行的建議(可以使它向後相容)是把類中的
改成這樣的語法糖:
但我不認同它把“self”變為保留字(reserved word),或者要求字首必須是“self”。如果這樣做了,那對於類方法,很容易也出現這種情況:
好了,相比於現狀,我並沒有更喜歡這個。但是相比於 Bruce 的提議或在他的部落格評論區中提出的更極端的說法,我認為這個要好得多,而且它具有向後相容的巨大優勢,並且不需要很費力,就可以寫成帶有參考實現的 PEP。(我想 Bruce 應該會發現自己提案中的缺陷,如果他真的付出努力嘗試編寫可靠的 PEP 或者嘗試實現它。
我可以繼續聊很多,但這是一個陽光明媚的週日早晨,而我還有其它的計劃... :-)
作者:Guido van Rossum,寫於:2008.10.26
參考資料

[1] Bruce博文:http://www.artima.com/weblogs/viewpost.jsp?thread=239003

[2] Guido原文: https://neopythonic.blogspot.com/2008/10/why-explicit-self-has-to-stay.html

【本文作者】

豌豆花下貓,某985高校畢業生, 兼具極客思維與人文情懷 。公眾號【Python貓】,專注python技術、資料科學和深度學習

- END -
如果看到這裡,說明你喜歡這篇文章,請轉發、點贊。微信搜尋「perfect_iscas」,關注後回覆「進群」或者掃描下方二維碼即可進入無廣告技術交流群。
掃描二維碼進群↓

640?wx_fmt=jpeg

640

640?wx_fmt=gif

在看 640?wx_fmt=jpeg

相關文章