<menu id="ycqsw"></menu><nav id="ycqsw"><code id="ycqsw"></code></nav>
<dd id="ycqsw"><menu id="ycqsw"></menu></dd>
  • <nav id="ycqsw"></nav>
    <menu id="ycqsw"><strong id="ycqsw"></strong></menu>
    <xmp id="ycqsw"><nav id="ycqsw"></nav>
  • python中的self參數(python中self用法詳解)


    我們知道,面向對象最重要的概念就是類(class)和實例(instance),類是抽象的模板,比如學生這個抽象的事物,可以用一個Student類來表示。而實例是根據類創建出來的一個個具體的“對象”,每一個對象都從類中繼承有相同的方法,但各自的數據可能不同。

    1、定義類

    以Student類為例,在Python中,定義類如下:

    class Student(object):
        pass

    (Object)表示該類從哪個類繼承下來的,Object類是所有類都會繼承的類。即class Name(*)中*是表示從哪個類繼承下來的

    2、由類創建實例

    定義好了類,就可以通過Student類創建出Student的實例,創建實例是通過類名+()實現:

    student = Student()

    3、添加屬性

    由于類起到模板的作用,因此,可以在創建實例的時候,把我們認為必須綁定的屬性強制填寫進去。這里就用到Python當中的一個內置方法__init__方法,例如在Student類時,把name、score等屬性綁上去:

    class Student(object):
    	  slogan = 'I ame a student'				# 類屬性:類名訪問
        def __init__(self, name, score):
            self.name =i name						  # 實例屬性 :實例與類名均可訪問
            self.score = score						# 實例屬性
    • __init__方法的第一參數永遠是self,表示類的實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因為self就指向創建的實例本身(通過self綁定的屬性是實例屬性)
    • 另外,這里self就是指由類創建的實例本身,self.name是Student類實例本身的屬性變量,而name是外部傳來的參數,所以,self.name = name的意思就是把外部傳來的參數name的值賦值給Student類實例本身自己的屬性變量self.name
    • 有了__init__方法,在創建實例的時候,就不能傳入空的參數了,必須傳入與__init__方法匹配的參數,但self不需要傳,Python解釋器會自己把實例變量傳進去
    >>>student = Student("Hugh", 99)
    >>>student.name
    "Hugh"
    >>>student.score
    99

    4、類中的函數與普通函數作比較

    和普通函數相比,在類中定義函數只有一點不同,就是第一參數永遠是類的本身實例變量self,并且調用時,不用傳遞該參數。除此之外,類的方法(函數)和普通函數沒啥區別,你既可以用默認參數、可變參數或者關鍵字參數(*args是可變參數,args接收的是一個tuple,**kw是關鍵字參數,kw接收的是一個dict)。

    5、類的方法

    既然Student類實例本身就擁有這些數據,那么要訪問這些數據,就沒必要從外面的函數去訪問,而可以直接在Student類的內部定義訪問數據的函數(方法),這樣,就可以把”數據”封裝起來。這些封裝數據的函數是和Student類本身是關聯起來的,稱之為類的方法(類實例與類均可訪問)

    class Student(obiect):
        def __init__(self, name, score):
            self.name = name
            self.score = score
        def print_score(self):
            print "%s: %s" % (self.name, self.score)
    >>>student = Student("Hugh", 99)
    >>>student.print_score
    Hugh: 99

    這樣一來,我們從外部看Student類,創建實例需要給出name和score。而如何打印,都是在Student類的內部定義的,這些數據和邏輯被封裝起來了,調用很容易,但卻不知道內部實現的細節。

    如果要讓內部屬性不被外部訪問,可以把屬性的名稱前加上兩個下劃線’__’,在Python中,實例的變量名如果以__開頭,就變成了一個私有變量(private),只有內部可以訪問,外部不能訪問,所以,我們把Student類改一改:

    class Student(object):
        def __init__(self, name, score):
            self.__name = name
            self.__score = score
        def print_score(self):
            print "%s: %s" %(self.__name,self.__score)

    改完后,對于外部代碼來說,沒什么變動,但是已經無法從外部訪問實例變量.__name和實例變量.__score了:

    >>> student = Student('Hugh', 99)
    >>> student.__name
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute '__name'

    這樣就確保了外部代碼不能隨意修改對象內部的狀態,這樣通過訪問限制的保護,代碼更加健壯。

    但是如果外部代碼要獲取name和score怎么辦?可以給Student類增加get_name和get_score這樣的方法:

    class Student(object):
        ...
        def get_name(self):
            return self.__name
        def get_score(self):
            return self.__score

    如果又要允許外部代碼修改score怎么辦?可以給Student類增加set_score方法:

    class Student(object):
        ...
        def set_score(self, score):
            self.__score = score

    需要注意的是,在Python中,變量名類似__xxx__的,也就是以雙下劃線開頭,并且以雙下劃線結尾的,是特殊變量,特殊變量是可以直接訪問的,不是private變量,所以,不能用__name__、__score__這樣的變量名。

    有些時候,你會看到以一個下劃線’_’開頭的實例變量名,比如_name,這樣的實例變量外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變量時,意思就是,“雖然我可以被訪問,但是,請把我視為私有變量,不要隨意訪問”。

    封裝的另一個好處是可以隨時給Student類增加新的方法,比如:get_grade:

    class Student(object):
        ...
        def get_grade(self):
            if self.score >= 90:
                return 'A'
            elif self.score >= 60:
                return 'B'
            else:
                return 'C'

    同樣的,get_grade方法可以直接在實例變量上調用,不需要知道內部實現細節:

    >>> student.get_grade()
    'A'

    6、self的仔細用法

    • (1)、self代表類的實例,而非類
    class Test:
        def ppr(self):
            print(self)
            print(self.__class__)
    t = Test()
    t.ppr()
    執行結果:
    <__main__.Test object at 0x000000000284E080>
    <class '__main__.Test'>

    從上面的例子中可以很明顯的看出,self代表的是類的實例。而self.__class__則指向類。

    注意:把self換成this,結果也一樣,但Python中最好用約定俗成的self。

    • (2)self可以不寫嗎?

    在Python解釋器的內部,當我們調用t.ppr()時,實際上Python解釋成Test.ppr(t),也就是把self替換成了類的實例

    class Test:
        def ppr():
            print(self)
    t = Test()
    t.ppr()
    運行結果如下:
    Traceback (most recent call last):
      File "cl.py", line 6, in <module>
        t.ppr()
    TypeError: ppr() takes 0 positional arguments but 1 was given

    運行時提醒錯誤如下:ppr在定義時沒有參數,但是我們運行時強行傳了一個參數。由于上面解釋過了t.ppr()等同于Test.ppr(t),所以程序提醒我們多傳了一個參數t。這里實際上已經部分說明了self在定義時不可以省略

    當然,如果我們的定義和調用時均不傳類實例是可以的,這就是類方法。

    class Test:
        def ppr():
            print(__class__)
    Test.ppr()
    運行結果:
    <class '__main__.Test'>
    • (3)在繼承時,傳入的是哪個實例,就是那個傳入的實例,而不是指定義了self的類的實例。
    class Parent:
        def pprt(self):
            print(self)
    class Child(Parent):
        def cprt(self):
            print(self)
    c = Child()
    c.cprt()
    c.pprt()			# 等同于Child.pprt(c)
    p = Parent()
    p.pprt()
    運行結果:
    <__main__.Child object at 0x0000000002A47080>
    <__main__.Child object at 0x0000000002A47080>
    <__main__.Parent object at 0x0000000002A47240>

    運行c.cprt()時應該沒有理解問題,指的是Child類的實例。

    但是在運行c.pprt()時,等同于Child.ppt(rc),所以self指的依然是Child類的實例,由于self中沒有定義pprt()方法,所以沿著繼承樹往上找,發現在父類Parent中定義了pprt()方法,所以就會成功調用。

    • (4)在描述符類中,self指的是描述符類的實例
    class Desc:
        def __get__(self, ins, cls):
            print('self in Desc: %s ' % self )
            print(self, ins, cls)
    class Test:
        x = Desc()
        def prt(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'>

    這里主要的疑問應該在:Desc類中定義的self不是應該是調用它的實例t嗎?怎么變成了Desc類的實例了呢?

    因為這里調用的是t.x,也就是說是Test類的實例t的屬性x,由于實例t中并沒有定義屬性x,所以找到了類屬性x,而該屬性是描述符屬性,為Desc類的實例而已,所以此處并沒有頂用Test的任何方法。

    那么我們如果直接通過類來調用屬性x也可以得到相同的結果。下面是把t.x改為Test.x運行的結果。

    self in Test: <__main__.Test object at 0x00000000022570B8>
    self in Desc: <__main__.Desc object at 0x000000000223E208>
    <__main__.Desc object at 0x000000000223E208> None <class '__main__.Test'>

    題外話:由于在很多時候描述符類中仍然需要知道調用該描述符的實例是誰,所以在描述符類中存在第二個參數ins(insctance),用來表示調用它的類實例,所以t.x時可以看到第三行中的運行結果中第二項為<main.Test object at 0x0000000002A570B8>。而采用Test.x進行調用時,由于沒有實例,所以返回None。

    總結

    • self在定義時需要定義,但是在調用時會自動傳入。
    • self的名字并不是規定死的,但是最好還是按照約定是用self
    • self總是指調用時的類的實例。

    版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。

    發表評論

    登錄后才能評論
    国产精品区一区二区免费