首頁  >  文章  >  後端開發  >  Python中的self怎麼使用

Python中的self怎麼使用

WBOY
WBOY轉載
2023-05-17 22:40:481872瀏覽

在介紹Python的self用法之前,先來介紹下Python中的類別和實例
我們知道,物件導向最重要的概念就是類別(class)和實例(instance),類別是抽象的模板,例如學生這個抽象的事物,可以用一個Student類別來表示。而實例是根據類別創建出來的一個個具體的“物件”,每個物件都從類別中繼承有相同的方法,但各自的資料可能不同。
1、以Student類別為例,在Python中,定義類別如下:

class Student(object):
    pass

(Object)表示該類別從哪個類別繼承下來的,Object類別是所有類別都會繼承的類別。

2、實例:定義好了類,就可以透過Student類別建立出Student的實例,建立實例是透過類別名稱()實作:

student = Student()

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

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

這裡注意:(1)、__init__方法的第一參數永遠是self,表示創建的類別實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因為self就指向創建的實例本身。 (2)、有了__init__方法,在創建實例的時候,就不能傳入空的參數了,必須傳入與__init__方法匹配的參數,但self不需要傳,Python解釋器會自己把實例變數傳進去:

>>>student = Student("Hugh", 99)
>>>student.name
"Hugh"
>>>student.score
99

另外,這裡self就是指類別本身,self.name就是Student類別的屬性變量,就是Student類別所有。而name是外部傳來的參數,不是Student類別所自帶的。故,self.name = name的意思就是把外部傳來的參數name的值賦值給Student類別自己的屬性變數self.name

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: &#39;Student&#39; object has no attribute &#39;__name&#39;

這樣就確保了外部程式碼不能隨意修改物件內部的狀態,這樣透過存取限制的保護,程式碼更加健壯。

但是如果外部程式碼要取得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 &#39;A&#39;
        elif self.score >= 60:
            return &#39;B&#39;
        else:
            return &#39;C&#39;

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

>>> student.get_grade()
&#39;A&#39;

6、self的仔细用法
(1)、self代表类的实例,而非类。

class Test:
    def ppr(self):
        print(self)
        print(self.__class__)

t = Test()
t.ppr()
执行结果:
<__main__.Test object at 0x000000000284E080>
<class &#39;__main__.Test&#39;>

从上面的例子中可以很明显的看出,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 4225fa317875f3e92281a7b1a5733569
    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 &#39;__main__.Test&#39;>

(3)、在继承时,传入的是哪个实例,就是那个传入的实例,而不是指定义了self的类的实例。

class Parent:
    def pprt(self):
        print(self)

class Child(Parent):
    def cprt(self):
        print(self)
c = Child()
c.cprt()
c.pprt()
p = Parent()
p.pprt()

运行结果:

3ba939ae2ab45e6446dfb7d3ccf3666f
3ba939ae2ab45e6446dfb7d3ccf3666f
37393a707a29b4383bd0bff8ef420bd6

解释:
运行c.cprt()时应该没有理解问题,指的是Child类的实例。
但是在运行c.pprt()时,等同于Child.pprt(c),所以self指的依然是Child类的实例,由于self中没有定义pprt()方法,所以沿着继承树往上找,发现在父类Parent中定义了pprt()方法,所以就会成功调用。

(4)、在描述符类中,self指的是描述符类的实例

class Desc:
    def __get__(self, ins, cls):
        print(&#39;self in Desc: %s &#39; % self )
        print(self, ins, cls)
class Test:
    x = Desc()
    def prt(self):
        print(&#39;self in Test: %s&#39; % self)
t = Test()
t.prt()
t.x

运行结果如下:

self in Test: 3072936af736f4c364572c56069bcf31
self in Desc: 12be4e5ac2680d4089b4937aa96557a0
12be4e5ac2680d4089b4937aa96557a0 3072936af736f4c364572c56069bcf31 1c85a171de3a3e74098b384eca9a5162

这里主要的疑问应该在: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 &#39;__main__.Test&#39;>

以上是Python中的self怎麼使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除