python 參數(shù)列表中的self 顯式不等于冗余
更新時(shí)間:2008年12月01日 18:44:43 作者:
Self in the Argument List: Redundant is not Explicit
self在區(qū)分全局變量/函數(shù)和對(duì)象中的成員變量/函數(shù)十分有用。例如,它提供了一種作用域機(jī)制,我個(gè)人認(rèn)為比Ruby的@和@@清晰多了,這可能是習(xí)慣使然吧,但它確實(shí)和C++、Java中的this很相似。
然而,self總是有令我困擾的地方,我以前在這里說(shuō)過(guò)—我曾幻想能在Python3中這些能得以改進(jìn),然后通常會(huì)引發(fā)一輪熱議并最終以人們所說(shuō)的“顯勝于隱”告終。
我在巴西的時(shí)候曾和Luciano Ramalho(巴西Python組織的主席)有過(guò)一次交談。他讓我明白并非無(wú)處不在的self讓我困擾不已,而是參數(shù)列表中的self,我想也稱(chēng)為非pythonic(un-pythonic)。
它是如何使用的
下面是一些簡(jiǎn)單的Python代碼,說(shuō)明了如何使用類(lèi)。
def f(): pass
a = 1
class C1(object):
a = 2
def m1(self):
print a # Prints '1'
print self.a # Prints '2'
f() # The global version
self.m2() # Must scope other members
def m2(self): pass
obj = C1()
obj.m1()
首先看f()和a,它們都可在全局作用域中調(diào)用。類(lèi)C1被定義成繼承自object,這是定義一個(gè)新類(lèi)的標(biāo)準(zhǔn)過(guò)程(我想在Python3中這些會(huì)變得更加不明顯)。
注意,m1()和m2()的第一個(gè)參數(shù)都是self。在Python中,self不是關(guān)鍵字。但按照慣例“self”代表當(dāng)前對(duì)象的地址,也就是對(duì)象的地址通常是第一個(gè)參數(shù)。
在類(lèi)范圍上定義a是創(chuàng)建對(duì)象作用域的方式之一。你也可以在a的method里賦值給self.a,并且第一次運(yùn)行該語(yǔ)句時(shí)就分配了這個(gè)域的內(nèi)存空間。但有必要區(qū)分兩種版本的a。若在method內(nèi)部使用a,那么這個(gè)a就是全局版本的,而self.a體現(xiàn)的是對(duì)象域(你也可以在類(lèi)內(nèi)部對(duì)全局變量進(jìn)行賦值,這里我暫不考慮這種情況)。
同樣地,一個(gè)對(duì)f()的非限定調(diào)用(unqualified call)造就了全局函數(shù),通過(guò)對(duì)其限定self.m2()調(diào)用的是成員函數(shù)(同時(shí)將當(dāng)前對(duì)象地址作為傳遞給m2()的self變量)。
現(xiàn)在來(lái)看一個(gè)含有帶參數(shù)的method的類(lèi):
class C2(object):
def m2(self, a, b): pass
為了調(diào)用該method,我們創(chuàng)建了一個(gè)對(duì)象實(shí)例,然后使用點(diǎn)表達(dá)式調(diào)用對(duì)象obj上的m2():
obj = C2()
obj.m2(1,2)
在調(diào)用過(guò)程中,obj的地址作為self變量在m2()中隱含傳遞,這里遇到了一個(gè)嚴(yán)重的矛盾:為何當(dāng)定義method時(shí)隱式好于顯式,而調(diào)用method時(shí)隱式也毫無(wú)問(wèn)題?
當(dāng)然我想這可能是method調(diào)用語(yǔ)法所要求的,但這就意味著method的定義和調(diào)用有很大不同,這里既沒(méi)有“顯式”也不pythonic。在調(diào)用參數(shù)個(gè)數(shù)錯(cuò)誤的method時(shí)就能看出來(lái):
obj.m2(1)
結(jié)果錯(cuò)誤提示為:
Traceback (most recent call last):
File "classes.py", line 9, in <module>
obj.m2(1)
TypeError: m2() takes exactly 3 arguments (2 given)
由于method調(diào)用期間self的隱式參數(shù)傳遞,上述錯(cuò)誤信息實(shí)際是說(shuō)應(yīng)該這樣調(diào)用method:
C2.m2(obj,1,2)
即使上面這行語(yǔ)句運(yùn)行成功,它當(dāng)然也不是實(shí)際的調(diào)用方式。你應(yīng)該使用常規(guī)的method調(diào)用語(yǔ)法,即傳遞兩個(gè)參數(shù):
obj.m2(1,2)
錯(cuò)誤信息“m2() takes exactly 3 arguments (2 given)”不僅讓初學(xué)者十分糊涂,我每次看到它后也常常懵住。我想這既表明了它是非Pythonic、也直指method定義和調(diào)用的矛盾。
絕望的建議
盡管漫長(zhǎng)歷史中盡是絕望,我又有哪些建議呢?
在Python3.1中增加self為關(guān)鍵字(有一點(diǎn)更加向后不兼容)(或直接使用this來(lái)使C++和Java程序員時(shí)更容易過(guò)渡)。而所有與self有關(guān)的已有規(guī)則都不變。
唯一的改變是:你不必將self放入method參數(shù)列表中。這是唯一隱式的地方,其它都是顯式的—除了依舊不變的method調(diào)用。
它實(shí)現(xiàn)了method定義和調(diào)用的一致性。你可以定義一個(gè)與調(diào)用時(shí)具有相同參數(shù)個(gè)數(shù)的method。當(dāng)調(diào)用method所傳遞參數(shù)個(gè)數(shù)出錯(cuò)時(shí),錯(cuò)誤信息會(huì)通知method應(yīng)含有的實(shí)際參數(shù)個(gè)數(shù),而不是多出一個(gè)。
顯式 vs.冗余
在我再一次聽(tīng)到“顯勝于隱”之前,讓某件事變得清晰和變得冗余還是有區(qū)別的。我們已有這樣一種語(yǔ)言:它讓你歷經(jīng)了無(wú)數(shù)考驗(yàn),原因就是起初看起來(lái)似乎很好但之后問(wèn)題卻越來(lái)越多。它叫做Java。
如果想讓每一件東西都變?yōu)轱@式,我們可以使用C或匯編以及其它能夠精確說(shuō)明和展現(xiàn)機(jī)器內(nèi)部運(yùn)行細(xì)節(jié)的語(yǔ)言。
強(qiáng)制程序員將self放入method參數(shù)列表與顯式根本不沾邊,它只是強(qiáng)制造成冗余的做法,也不會(huì)增加編程的表達(dá)方式(已經(jīng)知道是一個(gè)method了,何必還要在參數(shù)列表中加入self來(lái)提醒我們呢)。我認(rèn)為,它是死板的,也是非pythonic。
然而,self總是有令我困擾的地方,我以前在這里說(shuō)過(guò)—我曾幻想能在Python3中這些能得以改進(jìn),然后通常會(huì)引發(fā)一輪熱議并最終以人們所說(shuō)的“顯勝于隱”告終。
我在巴西的時(shí)候曾和Luciano Ramalho(巴西Python組織的主席)有過(guò)一次交談。他讓我明白并非無(wú)處不在的self讓我困擾不已,而是參數(shù)列表中的self,我想也稱(chēng)為非pythonic(un-pythonic)。
它是如何使用的
下面是一些簡(jiǎn)單的Python代碼,說(shuō)明了如何使用類(lèi)。
復(fù)制代碼 代碼如下:
def f(): pass
a = 1
class C1(object):
a = 2
def m1(self):
print a # Prints '1'
print self.a # Prints '2'
f() # The global version
self.m2() # Must scope other members
def m2(self): pass
obj = C1()
obj.m1()
首先看f()和a,它們都可在全局作用域中調(diào)用。類(lèi)C1被定義成繼承自object,這是定義一個(gè)新類(lèi)的標(biāo)準(zhǔn)過(guò)程(我想在Python3中這些會(huì)變得更加不明顯)。
注意,m1()和m2()的第一個(gè)參數(shù)都是self。在Python中,self不是關(guān)鍵字。但按照慣例“self”代表當(dāng)前對(duì)象的地址,也就是對(duì)象的地址通常是第一個(gè)參數(shù)。
在類(lèi)范圍上定義a是創(chuàng)建對(duì)象作用域的方式之一。你也可以在a的method里賦值給self.a,并且第一次運(yùn)行該語(yǔ)句時(shí)就分配了這個(gè)域的內(nèi)存空間。但有必要區(qū)分兩種版本的a。若在method內(nèi)部使用a,那么這個(gè)a就是全局版本的,而self.a體現(xiàn)的是對(duì)象域(你也可以在類(lèi)內(nèi)部對(duì)全局變量進(jìn)行賦值,這里我暫不考慮這種情況)。
同樣地,一個(gè)對(duì)f()的非限定調(diào)用(unqualified call)造就了全局函數(shù),通過(guò)對(duì)其限定self.m2()調(diào)用的是成員函數(shù)(同時(shí)將當(dāng)前對(duì)象地址作為傳遞給m2()的self變量)。
現(xiàn)在來(lái)看一個(gè)含有帶參數(shù)的method的類(lèi):
復(fù)制代碼 代碼如下:
class C2(object):
def m2(self, a, b): pass
為了調(diào)用該method,我們創(chuàng)建了一個(gè)對(duì)象實(shí)例,然后使用點(diǎn)表達(dá)式調(diào)用對(duì)象obj上的m2():
復(fù)制代碼 代碼如下:
obj = C2()
obj.m2(1,2)
在調(diào)用過(guò)程中,obj的地址作為self變量在m2()中隱含傳遞,這里遇到了一個(gè)嚴(yán)重的矛盾:為何當(dāng)定義method時(shí)隱式好于顯式,而調(diào)用method時(shí)隱式也毫無(wú)問(wèn)題?
當(dāng)然我想這可能是method調(diào)用語(yǔ)法所要求的,但這就意味著method的定義和調(diào)用有很大不同,這里既沒(méi)有“顯式”也不pythonic。在調(diào)用參數(shù)個(gè)數(shù)錯(cuò)誤的method時(shí)就能看出來(lái):
obj.m2(1)
結(jié)果錯(cuò)誤提示為:
Traceback (most recent call last):
File "classes.py", line 9, in <module>
obj.m2(1)
TypeError: m2() takes exactly 3 arguments (2 given)
由于method調(diào)用期間self的隱式參數(shù)傳遞,上述錯(cuò)誤信息實(shí)際是說(shuō)應(yīng)該這樣調(diào)用method:
C2.m2(obj,1,2)
即使上面這行語(yǔ)句運(yùn)行成功,它當(dāng)然也不是實(shí)際的調(diào)用方式。你應(yīng)該使用常規(guī)的method調(diào)用語(yǔ)法,即傳遞兩個(gè)參數(shù):
obj.m2(1,2)
錯(cuò)誤信息“m2() takes exactly 3 arguments (2 given)”不僅讓初學(xué)者十分糊涂,我每次看到它后也常常懵住。我想這既表明了它是非Pythonic、也直指method定義和調(diào)用的矛盾。
絕望的建議
盡管漫長(zhǎng)歷史中盡是絕望,我又有哪些建議呢?
在Python3.1中增加self為關(guān)鍵字(有一點(diǎn)更加向后不兼容)(或直接使用this來(lái)使C++和Java程序員時(shí)更容易過(guò)渡)。而所有與self有關(guān)的已有規(guī)則都不變。
唯一的改變是:你不必將self放入method參數(shù)列表中。這是唯一隱式的地方,其它都是顯式的—除了依舊不變的method調(diào)用。
它實(shí)現(xiàn)了method定義和調(diào)用的一致性。你可以定義一個(gè)與調(diào)用時(shí)具有相同參數(shù)個(gè)數(shù)的method。當(dāng)調(diào)用method所傳遞參數(shù)個(gè)數(shù)出錯(cuò)時(shí),錯(cuò)誤信息會(huì)通知method應(yīng)含有的實(shí)際參數(shù)個(gè)數(shù),而不是多出一個(gè)。
顯式 vs.冗余
在我再一次聽(tīng)到“顯勝于隱”之前,讓某件事變得清晰和變得冗余還是有區(qū)別的。我們已有這樣一種語(yǔ)言:它讓你歷經(jīng)了無(wú)數(shù)考驗(yàn),原因就是起初看起來(lái)似乎很好但之后問(wèn)題卻越來(lái)越多。它叫做Java。
如果想讓每一件東西都變?yōu)轱@式,我們可以使用C或匯編以及其它能夠精確說(shuō)明和展現(xiàn)機(jī)器內(nèi)部運(yùn)行細(xì)節(jié)的語(yǔ)言。
強(qiáng)制程序員將self放入method參數(shù)列表與顯式根本不沾邊,它只是強(qiáng)制造成冗余的做法,也不會(huì)增加編程的表達(dá)方式(已經(jīng)知道是一個(gè)method了,何必還要在參數(shù)列表中加入self來(lái)提醒我們呢)。我認(rèn)為,它是死板的,也是非pythonic。
相關(guān)文章
Python動(dòng)態(tài)語(yǔ)言與鴨子類(lèi)型詳解
這篇文章主要介紹了Python動(dòng)態(tài)語(yǔ)言與鴨子類(lèi)型詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
Python Matplotlib繪制箱線(xiàn)圖的全過(guò)程
又稱(chēng)箱形圖(boxplot)或盒式圖,數(shù)據(jù)大小、占比、趨勢(shì)等等的呈現(xiàn)其包含一些統(tǒng)計(jì)學(xué)的均值、分位數(shù)、極值等等統(tǒng)計(jì)量,因此該圖信息量較大,下面這篇文章主要給大家介紹了關(guān)于Python Matplotlib繪制箱線(xiàn)圖的相關(guān)資料,需要的朋友可以參考下2021-09-09
python安裝和pycharm環(huán)境搭建設(shè)置方法
這篇文章主要介紹了python安裝和pycharm環(huán)境搭建和設(shè)置方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下 ,2020-05-05
Python如何查看兩個(gè)數(shù)據(jù)庫(kù)的同名表的字段名差異
這篇文章主要介紹了Python如何查看兩個(gè)數(shù)據(jù)庫(kù)的同名表的字段名差異,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Python?SQLAlchemy之SQL工具包和ORM的用法詳解
SQLAlchemy?是?Python?中一款非常流行的數(shù)據(jù)庫(kù)工具包,它對(duì)底層的數(shù)據(jù)庫(kù)操作提供了高層次的抽象,在本篇文章中,我們將介紹SQLAlchemy的兩個(gè)主要組成部分:SQL工具包和對(duì)象關(guān)系映射器的基本使用,需要的朋友可以參考下2023-08-08
tensorflow建立一個(gè)簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò)的方法
本篇文章主要介紹了tensorflow建立一個(gè)簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02

