原文地址https://blog.csdn.net/love666666shen/article/details/78189984
Python中的self
在Python中的類Class的代碼中,常看到函數中的第一個參數,都是self;
同時Class中的函數里面,訪問對應的變量(讀取或者寫入),以及調用對應的函數時,都是self.valueName,self.function()的形式。
不適用類Class直接編寫函數時倒沒有注意,一旦編寫類,調用其中的函數是老是出現參數或多或少的情況,這時候才回過頭來深入看了看self的含義和具體的使用。
Python中self的含義
self,
英文單詞意思很明顯,表示自己,本身。此處有幾種潛在含義:
1.這里的自己,指的是,實例Instance本身。
2.同時, 由於說到“自己”這個詞,都是和相對的“其他”而說的。此處的其他,指的是,類Class,和其他變量,比如局部變量,全局變量等。
此處的self,是個對象,Object。是當前類的實例。因此,對應的self.valueName,self.function()中的valueName和function()具體含義如下:
valueName:表示self對象,即實例的變量。與其他的Class的變量,全局的變量,局部的變量,是相對應的。
function:表示是調用的是self對象,即實例的函數。與其他的全局的函數,是相對應的。
Python中為何要有self
如果沒有在__init__中初始化對應的實例變量的話,導致后續引用實例變量會出錯
如下代碼,完整的演示了,如果沒有在類Class的最初的__init__函數中,正確的初始化實例變量,則會導致后續沒有變量可用,因而出現AttributeError的錯誤:
#!/usr/bin/python # -*- coding: utf-8 -*- #注:此處全局的變量名,寫成name,只是為了演示而用 #實際上,好的編程風格,應該寫成gName之類的名字,以表示該變量是Global的變量 name ="whole global name"; classPerson: def__init__(self, newPersonName): #self.name = newPersonName; #1.如果此處不寫成self.name #那么此處的name,只是__init__函數中的局部臨時變量name而已 #和全局中的name,沒有半毛錢關系 name =newPersonName;#此處的name,只是__init__函數中的局部臨時變量name而已 #此處只是為了代碼演示,而使用了局部變量name, #不過需要注意的是,此處很明顯,由於接下來的代碼也沒有利用到此處的局部變量name #則會導致:此處的name變量,實際上被浪費了,根本沒有被使用 defsayYourName(self): #此處由於找不到實例中的name變量,所以會報錯: #AttributeError: Person instance has no attribute 'name' print'My name is %s'%(self.name); defselfAndInitDemo(): personInstance =Person("crifan"); personInstance.sayYourName(); ############################################################################### if__name__=="__main__": selfAndInitDemo();
從上述代碼可見,由於在類的初始化(實例化)的__init__函數中,沒有給self.name設置值,使得實例中,根本沒有name這個變量,導致后續再去訪問self.name,就會出現AttributeError的錯誤。
對應的,如果寫成self.name就正確了,就是初始化的時候,在實例中新增加並且設置了正確的值newPersionName,所以后續再去通過self.name,就可以訪問到當前實例中正確的變量name了。
相應的正確寫法的代碼如下:
#注:此處全局的變量名,寫成name,只是為了演示而用 #實際上,好的編程風格,應該寫成gName之類的名字,以表示該變量是Global的變量 name ="whole global name"; classPerson: def__init__(self, newPersionName): #相當於java中的Person類的構造方法 #此處正確的,通過訪問self.name的形式,實現了: #1.給實例中,增加了name變量 #2.並且給name賦了初值,為newPersonName self.name =newPersonName; defsayYourName(self): #此處由於開始正確的初始化了self對象,使得其中有了name變量,所以此處可以正確訪問了name值了,可以正確的輸出了: #My name is crifan print'My name is %s'%(self.name); defselfAndInitDemo(): personInstance =Person("crifan"); personInstance.sayYourName();
############################################################################### if__name__=="__main__": selfAndInitDemo();
有時候,雖然寫的代碼,可以運行,但是使用到的變量,由於沒有加self,實際上用到的不是實例變量,而是其他的變量。
此類問題,主要和Python中的變量的作用域有關,但在下面例子中,也和是否使用self有關:
#注:此處全局的變量名,寫成name,只是為了演示而用 #實際上,好的編程風格,應該寫成gName之類的名字,以表示該變量是Global的變量 name ="whole global name"; classPerson: name ="class global name" def__init__(self, newPersonName): #self.name = newPersonName; #此處,沒有使用self.name #而使得此處的name,實際上仍是局部變量name #雖然此處賦值了,但是后面沒有被利用到,屬於被浪費了的局部變量name name =newPersonName; defsayYourName(self): #此處,之所以沒有像之前一樣出現: #AttributeError: Person instance has no attribute 'name' #那是因為,雖然當前的實例self中,沒有在__init__中初始化對應的name變量,實例self中沒有對應的name變量 #但是由於實例所對應的類Person,有對應的name變量,所以也是可以正常執行代碼的 #對應的,此處的self.name,實際上是Person.name print'My name is %s'%(self.name); # -> class global name print'name within class Person is actually the global name: %s'%(name); #-> whole global name print"only access Person's name via Person.name=%s"%(Person.name); # -> class global name defselfAndInitDemo(): personInstance =Person("crifan"); personInstance.sayYourName(); print"whole global name is %s"%(name); # -> whole global name ############################################################################### if__name__=="__main__": selfAndInitDemo();
可見,此處__init__中,沒有給self實例初始化對應的name,而在后面的函數sayYourName中,雖然可以調用到self.name但並沒有出現AttributeError錯誤。
然而,實際上此處的值,不是所期望傳入的name,即"crifan",而是類中的name的值,即"class global name"。
self代表類的實例,而非類。
self不必非寫成self
self可以不寫嗎
在Python的解釋器內部,當我們調用t.prt()時,實際上Python解釋成Test.prt(t),也就是說把self替換成類的實例。
有興趣的童鞋可以把上面的t.prt()一行改寫一下,運行后的實際結果完全相同。
實際上已經部分說明了self在定義時不可以省略,如果非要試一下,那么請看下面:
classTest:
defprt(): print(self) t=Test() t.prt()
當然,如果我們的定義和調用時均不傳類實例是可以的,這就是類方法。
classTest: defprt(): print(__class__) Test.prt()
運行結果如下
<class '__main__.Test'>
在繼承時,傳入的是哪個實例,就是那個傳入的實例,而不是指定義了self的類的實例。 先看代碼 classParent: defpprt(self): print(self) classChild(Parent): defcprt(self): print(self) c=Child() c.cprt() c.pprt() p=Parent() p.pprt()
在描述父類中,self指的是描述父類的實例
不太容易理解,先看實例:
classDesc: def__get__(self,ins,cls): print('self in Desc: %s '%self) print(self,ins,cls) classTest: x=Desc() defprt(self): print('self in Test: %s'%self) t=Test() t.prt() t.x
運行結果如下:
self in Test: <__main__.Test object at 0x0000000002A570B8> self in Desc: <__main__.Desc object at 0x000000000283E208> <__main__.Desc object at 0x000000000283E208> <__main__.Test object at 0x0000000002A570B8> <class '__main__.Test'>
總結
self在定義時需要定義,但是在調用時會自動傳入。
self的名字並不是規定死的,但是最好還是按照約定是用self
self總是指調用時的類的實例。
用代碼演示如下:
classPerson: def__init__(self, newPersonName): #在開始初始化新的類Class的示例Instance的時候,給對應的,不同的Instance,設置不同的人名(Person name) self.name =newPersonName; defsayYourName(self): #不同的Person的示例,調用同樣的方法的時候,說出自己的名字,結果都是對應着自己的,各自不同的名字 print'My name is %s'%(self.name); #My name is crifan definitDemo(): persionInstance =Person("crifan"); persionInstance.sayYourName(); ############################################################################### if__name__=="__main__": initDemo();
其中,針對Person這個類,不同的示例,在初始化的時候,傳遞一個對應的參數,這樣不同的Person,就都有了自己的不同的名字了。
這看起來,有點類似於其他語言中,通過傳遞特定參數去對類進行初始化。
個人理解小結:
首先明確的是self只有在類的方法中才會有,獨立的函數或方法是不必帶有self的。self在定義類的方法時是必須有的,雖然在調用時不必傳入相應的參數。
self名稱不是必須的,在python中self不是關鍵詞,你可以定義成a或b或其它名字都可以,但是約定成俗,不要搞另類,大家會不明白的。
下例中將self改為myname一樣沒有錯誤:
class Person: def __init__(myname,name): myname.name=name def sayhello(myname): print 'My name is:',myname.name p=Person('Bill') print p self指的是類實例對象本身(注意:不是類本身)。 class Person: def __init__(self,name): self.name=name def sayhello(self): print 'My name is:',self.name p=Person('Bill') print p 在上述例子中,self指向Person的實例p。 為什么不是指向類本身呢,如下例子: class Person: def __init__(self,name): self.name=name def sayhello(self): print 'My name is:',self.name p=Person('Bill') p1 = Person('Apple') print p 如果self指向類本身,那么當有多個實例對象時,self指向哪一個呢?所以,self代表的是具體的實例化對象,這樣當有多個實例化對象時才不至於混淆不知道調用的是哪一個實例的方法或屬性。