讀懂python中的self


  在Python類中規定,函數的第一個參數是實例對象本身,無論是顯式創建類的構造方法,還是向類中添加實例方法,都要求將 self 參數作為方法的第一個參數,並且約定俗成,把其名字寫為self。例如定義一個Chinese類:
class Chinese: country = 'China'
    def __init__(self, name, age): self.name = name self.age = age def talk(self): print(self, 'is talking Chinese') if __name__ == '__main__': DY = Chinese("DY", "18") DY.talk()
  但Python中並沒有規定該參數的具體名稱,之所以將其命名為 self,只是程序員之間約定俗成的一種習慣,遵守這個約定,可以使我們編寫的代碼具有更好的可讀性(大家一看到 self,就知道它的作用)。

1.類的實例方法中self存在必要性

從實例與類關系來說
  self 參數的具體作用是什么呢?打個比方,如果把類比作造房子的圖紙,那么類實例化后的對象是真正可以住的房子。根據一張圖紙(類),我們可以設計出成千上萬的房子(類對象),每個房子長相都是類似的(都有相同的類變量和類方法),但它們都有各自的主人,那么如何對它們進行區分呢?
  當然是通過 self 參數,它就相當於每個房子的門鑰匙,可以保證每個房子的主人僅能進入自己的房子(每個類對象只能調用自己的類變量和類方法)。
  也就是說同一個類可以產生多個對象,當某個對象調用類方法時,該對象會把自身的引用作為第一個參數自動傳給該方法,換句話說,Python 會自動綁定類方法的第一個參數指向調用該方法的對象。如此,Python解釋器就能知道到底要操作哪個對象的方法。
  因此得出結論:無論是創建類的構造方法還是實例方法,最少要包含一個參數self。
 
  通過實例的self參數與對象進行綁定,程序在調用實例方法和構造方法時,也不需要手動為第一個參數傳值。所以我們在調用成員方法時可以寫成:
class Chinese:
    country = 'China'
    def __init__(self, name, age):
        self.name = name 
        self.age = age 
    def talk(self):
        print(self, 'is talking Chinese')
if __name__ == '__main__':
   DY = Chinese("DY", "18")
   DY.talk()
 
從實例方法存儲內存空間來講
  對象在實例化的時候,構造函數會開辟一塊內存空間,把屬性存到DY那個對象那里,實例方法還是存儲在類的方法區,對象直接去引用就行,原因在於copy出來函數執行代碼效果是不變的。實例化出來的對象如果想調用類的方法,就需要到Chinese類中去取。
  但有個問題,如果不告訴類是誰在調用實例方法,那么調用方法產生的結果不知道返回給哪個對象。因此我們在調用Chinese. talk(self)的時候應該告訴類是哪個對象在調用,所以最簡單的辦法是把對象傳進去。那么調用實例方法時還可以寫成:
class Chinese:
    country = 'China'
    def __init__(self, name, age):
        self.name = name 
        self.age = age 
    def talk(self):
        print(self, 'is talking Chinese')
if __name__ == '__main__':
   DY = Chinese("DY", "18")
   Chinese.talk(DY)
所以在調用實例方法時,都需要把self代表的對象傳進去,因此這個self就是誰調用這個方法,表示的就是哪個對象。

2. self是誰:指實際調用該方法的對象

上面說到類的實例方法(函數)中一定要有傳入一個參數self,把實例方法與調用該方法的對象進行綁定,那么怎么證明self指的就是該對象呢?
class Chinese:
    country = 'China'
    def __init__(self, name, age):
        self.name = name 
        self.age = age 
    def talk(self):
        print(self, 'is talking Chinese')
if __name__ == '__main__':
    DY = Chinese("DY", "18")   # 把一個類變成一個具體對象的過程叫類的實例化
    zhang = Chinese("zhang", "18")
    print(DY)  # 打印對象,返回對象所在的內存地址
    print(zhang)
    DY.talk()  # 打印對象的實例方法,獲取self的值    
    zhang.talk()
    
執行結果:
<__main__.Chinese object at 0x000001C718A1DA88> 
<__main__.Chinese object at 0x000001C718A1FF08> 
<__main__.Chinese object at 0x000001C718A1DA88> is talking Chinese 
<__main__.Chinese object at 0x000001C718A1FF08> is talking Chinese
通過執行結果我們可以看出:
①打印對象DY返回的結果<__main__.Chinese object at 0x00000269E594EEB8> ,0x00000269E594EEB8指的是該對象所在的內存地址,與通過對象調用實例方法(DY.talk()),打印的self的值0x00000269E594EEB8一致,可以看出他們指向的是同一內存地址,因此可得出結論:在當前實例化中self代表是對象DY。
②調用實例方法DY.talk()與zhang.talk()打印出self的內存地址分別為0x000001C718A1DA88和0x000001C718A1FF08,也就是說不同實例方法self的並不是同一個對象,DY.talk()中self指的是DY對象,zhang.talk()中self指的是zhang對象。
綜合以上得出:self指的是實際調用該方法的對象。

3. self不必非寫成self

上面代碼self更改為this:
class Chinese:
    country = 'China'
    def __init__(this, name, age):
        this.name = name
        this.age = age
    def talk(this):
        print(this, 'is talking Chinese')
if __name__ == '__main__':
    DY = Chinese("DY", "18")
    DY.talk()
  改成this后,運行結果完全一樣。我們只是使用self代指調用方法的對象,完成對象和實例方法的第一個參數進行綁定,至於實例方法的第一個參數寫成a,b,c.....或者this完全不受影響。
當然,最好還是尊重約定俗成的習慣,使用self。

4. self可以不寫嗎

我們試下實例方法(函數)中不傳入參數self:
class Chinese:
    def talk():
        print("我熱愛我的祖國")

if __name__ == '__main__':
    DY = Chinese()
    DY.talk()
    
執行結果:
Traceback (most recent call last): 
  File "test.py", line 577, in <module> 
    DY.talk() 
TypeError: talk() takes 0 positional arguments but 1 was given
  執行結果的意思是:TypeError: talk()接受0個位置參數,但給出了1個。 但是我們在調用實例方法的時候並沒有給函數傳入參數,給出的這1個參數是哪里來的?
  上面我們有說到實例方法與類的靜態屬性(實例變量)不同,實例變量會在實例化對象時存儲在對象的內存區間,但實例方法是存儲在類的方法區,只有實例方法被調用的時候才會被加載,所以我們在執行DY.talk()時,Python解釋器解釋為Chinese.talk(DY),把self替換成類的實例,這樣就解釋了上面的問題,給出的這一個位置參數就是self,也就是調用方法的對象。
所以,實例方法中必須要傳入一個形參,不可以不寫self。

5. 實例方法中的變量什么時候前綴加self

  類中的變量分為類變量、實例變量、局部變量。類變量就是定義在方法(函數)外的變量,在方法中可以使用類名進行調用,在類方法中可以使用cls.變量名進行調用,相對來說好做區分。實例變量和局部變量都是存在於方法中(函數內),那么大家肯定會有個疑問:變量前什么時候加self(實例變量),什么時候不加self(局部變量)?或者為了以防萬一變量前全都加self,如下:
class TestLogin(unittest.TestCase):
    def test_login(self):
         self.url = xxx
         self.resp = self.session.get(self.url)
         self.text = self.resp.text
         self.status = self.resp.statuscode
  但這樣很明顯沒有意義,url/resp/text/status這些變量都是局部的,別的方法里面不需要訪問這些變量,只存在於test_login函數中,別的用例也不需要使用這些變量,因此除了session屬性需要共用以外,其他變量前不需要加self。
  我們只需記住:前綴帶self的變量,就是在整個類的代碼塊里面類似是作為全局變量,如果變量前面加了self,那么在任何實例方法(非staticmethod和calssmethod)就都可以訪問這個變量了,如果沒有加self,只有在當前函數內部才能訪問這個變量。

總結

  • self在定義實例方法時需要傳入該參數,但是在調用時會自動傳入。
  • self的名字並不是規定死的,但是最好還是按照約定是用self。
  • self總是指調用該方法的實例。
  • 實例方法中需要在全局引用的變量需要加前綴self。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM