之前十幾篇端出連串理論和術語的「大魚大肉」,讀者可能已經吃膩。今天換碟清粥小菜,幫大家清清腸胃。
這碟清粥小菜,是 self
和 cls
。
self
和cls
這兩個參數的不同使用場合:
self
。cls
。各位應該輕易猜到,這cls
就是class的縮寫。self
是傳入物件的「本尊」。這應該是Python的ABC了,我猜大概隨便一本Python的教科書都有提及。所謂「本尊」,講白點就是在記憶體中的位置,即用id()函數取得的值。class Tree():
def __init__(self, breed, age):
self.__breed = breed
self.__age = age
print(f'class : {id(self)=}')
tree = Tree('cedar', 200)
print(f'parent: {id(tree)=}')
cls
則是「類別本身」。亦即類別在記憶體中的位置,同樣可用id()函數檢查:
class Tree():
__count = 0 # 放在constructor外面的是class attributes。
def __init__(self, breed: str, age: int):
self.__breed = breed
self.__age = age
Tree.__count += 1
@classmethod
def show_class_id(cls):
print(f'class : {id(cls) =}')
tree = Tree('cedar', 200)
print(f'parent: {id(Tree)=}')
Tree.show_class_id()
cls
位址相同,表示筆者所言不虛。本節講的 self
包括 cls
。
其實不只是Python,其他一些物件導向程式語言也會用self
或其他字眼代表物件本尊。大體上泰半程式語言非self
即this
。
其他語言這個self
或this
多半是強制的,而Python的self
卻僅是「慣例」。
再引用一次PEP 8有關self
和cls
的規範:
Always use
self
for the first argument to instance methods.
Always usecls
for the first argument to class methods.
會在PEP 8上規範,就知道不是強制了。
既然並未取得語法層面的「法定地位」,就表示不一定要用 self
。事實上任何Python的合法變數名稱都可以。
所以您真看self
不順眼,或覺得self
不吉利,可隨意改用諸如this
, that
, it
, Me
, Current
...等等。至於效果會不會比機房放乖乖好,不得而知。
甚至用更「無厘頭」的a
, b
, c
, x
, y
, z
或者您自己的大名也行。當然這種命名方法可不可取,是另一個議題了。請記住我們不是在寫擾亂器(obfuscator)。
證明:
class Tree():
# __count = 0 # 放在constructor外面的是class attributes。
def __init__(this, breed): # use 'this' instead of 'self'.
this.__breed = breed
def show_myself(that): # use 'that' instead of 'self'.
print(f'{id(that) = }')
def get_id(Tree): # Even class name 'Tree' is fine.
return id(Tree)
@classmethod
def show_class_id(alex):
print(f'class : {id(alex) =}')
tree = Tree('cedar')
Tree.show_class_id()
執行上面的code,無任何錯誤(輸出甚麼不重要),表示用this
, that
, Tree
, alex
...等等,在語法層面都是合法的。
真正的大問題在於:只要用到self
或cls
以外的名稱(注意兩者使用時機不同,不能混用),您就觸犯了PEP 8金科玉律。「問斬」倒不必,coding風格和其他大多數人不一樣則是肯定。不知這叫「別具創意」還是「標新立異」?
就看您自己取捨了。問筆者的話,我會強烈建議「西瓜靠大邊」,從眾用self
。筆者自己做專案打死也是self
來self
去的,謝絕其他字眼。
有人說coding是「藝術」,筆者從不相信。coding只是「工藝品」或「工業產品」,得遵守一定的規格和標準。Coding style不宜帶有強烈獨突的「個人風格和色彩」,最少筆者不能接受。
self
(含cls
,下同)這個名稱可以換用其他字眼,言外之意就是:self
並不是Python的保留字。而C++, Java, C#, JS這掛語言,和self
意義相當的this
卻是保留字。
Python只有35個保留字(註1)。C++有95個,Java 97個,C#較少,也有79個。號稱「最純」物件導向程式語言Smalltalk的保留字更少,只有5或6個。站在方便寫程式的角度,似乎越少保留字,為變數、物件命名的自由度越大。但站在其他角度看問題,就不一定越少越好。
正因為self
並非Python的保留字,所以必須在方法定義的參數列外顯標明self
作為第一個「形參」(formal parameter)(註2)。C-like語言則不需標明this
,因為那是人家的保留字。
弔詭的是,主程式呼叫(調用)方法時,「實參」(actual argument)卻不必傳出self
或其他變數。這個「本尊」由Python幫我們自動傳給方法。
結果就是目前的情形:類別中的方法,形參永遠要比實參多出一個。而一般的函數,實參和形參數目相等。當然這裡先跳過不談預設參數,*
參數,**
參數等情形。
請看下面的code:
class Tree():
def __init__(self, breed):
self.__breed = breed
def get_breed(self):
return self.__breed
tree = Tree('cedar') # 我們只傳一個參數給Tree(),其實Python幫我們傳了兩個。
print(f'{id(tree)=}')
print(f"tree.breed: {tree.get_breed()}") # 表面上沒有傳參數給get_bread(),其實Python內部傳出了物件本尊。
tree = Tree('cedar')
這行,我們只傳一個參數給Tree(),其實Python幫我們傳了兩個。print(f"tree.breed: {tree.get_breed()}")
這行,表面上沒有傳參數給get_bread(),其實Python內部傳出了物件本尊。self
和cls
兩個保留字。self
和cls
列為保留字,這樣函數定義地方就不必寫self或cls。形參和實參數目也相等。或者統一用另外一字例如this
取代self
和cls
也行。註1: 下表是Python的35個保留字。一般找到的表格都按字母排列,筆者覺得不甚理想,改依性質和功能重新整理排列,方便大家查閱:
True | False | None | from | import | as | with |
---|---|---|---|---|---|---|
global | nonlocal | is | in | and | or | not |
if | else | elif | for | while | break | continue |
def | return | yield | lambda | class | pass | del |
try | except | finally | raise | assert | async | await |
註2: 程式語言中的「實參」和「形參」說明:
argument
和parameter
可以混用,也常常混用。不管實參或者形參,都可以稱作argument或parameter。筆者認為,如果不是嚴謹的學術文章,不一定要分得那麼清楚。