解開Python中self的四個秘密

self的謎團

毫無疑問,幾乎每一門Python課程都有一個關於(class)類的講座——面向物件程式語言的基本構建模組之一。

當您透過一些示例學習它時,您將注意到在Python類中定義的許多函式都將self作為它們的第一個引數。例如,在下面的程式碼片段中,我們聲明瞭一個名為Student的類,它的greet()方法的第一個引數是self。但是,函式根本沒有使用self,所以這裡的self到底是從哪裡來的呢?這對許多初學者來說是第一個謎。

>>> class Student:。。。 def greet(self, name):。。。 print(‘Good Morning, ’ + name)。。。 >>> student = Student()>>> student。greet(‘John’)Good Morning, John

同樣奇怪的是,當我們使用這個函式時,我們並沒有給self引數設定任何東西,這是困擾我們的另一個謎題。在這篇文章中,我們將與學習者分享一些Python中self的奧秘。

解開Python中self的四個秘密

1。 它代表什麼?

在開始處理這個謎題之前,我們需要理解兩個基本的相關概念:類和例項。當然,解決所有這些謎題需要額外的知識,而不僅僅是類和例項,我將在接下來的討論中澄清這一點。如果你對這兩個概念都很瞭解,你可以跳過下一段,這段只是對這兩個概念的簡要概述。

建立Python類就是宣告一種新的物件型別,它提供了一種將資料和功能捆綁在一起的機制。在上面的示例中,我們建立了一個名為Student的類,並使用它建立了一個名為Student的學生型別的物件。這個物件被稱為student類的例項。此外,類還可以提供通常稱為屬性的特定功能,例如示例中的greet()函式。我們使用三個內省(introspection)函式(type()、isinstance()和hasattr())來檢查相關資訊。

>>> type(Student)>>> type(student)>>> isinstance(student, Student)True>>> hasattr(Student, ‘greet’)True

我可以簡單地告訴您,greet()函式中的self引數是上面示例中的student例項。更一般地說,是例項呼叫這個函式。以下是支援證據:

>>> class Student:。。。 def greet(self, name):。。。 print(id(self))。。。 print(‘Good Morning, ’ + name)。。。 >>> student = Student()>>> student。greet(‘John’)4546580944Good Morning, John>>> id(student)4546580944

在上面的程式碼中,我們修改了greet()函式,要求它使用內省id()函式向我們顯示self引數的記憶體地址。如您所見,self引數和例項student是同一個物件,因為它們具有相同的記憶體地址。

解開Python中self的四個秘密

2。 為什麼不需要在函式呼叫中設定它呢?

繼續上一節中展示的示例,當我們使用例項student呼叫greet()函式時,這個函式通常被稱為例項方法——一個對某個類的例項可用的函式。但是,如果我們檢查這個屬性的型別,就會顯示一些不同的東西。

>>> student = Student()>>> student。greet>

如上所述,例項student的greet屬性稱為繫結方法。具體來說,它被繫結到Student類的greet屬性。

為了準確理解這意味著什麼,讓我們看看下面的程式碼:

>>> Student。greet(student, ‘John’)Good Morning, John

結合開頭的示例,您可能會注意到這段程式碼中的三件事:

這個函式的呼叫者是類Student,而不是例項student。

在這個呼叫中設定了self和name引數,這與student呼叫初始函式時忽略self引數不同。

兩個函式呼叫都產生了相同的輸出。它們本質上用的是同一個函式。

透過實現這些資訊,您可能已經猜到在使用例項student呼叫greet()函式時,幕後發生了什麼。

解開Python中self的四個秘密

如上圖所示,當例項student呼叫greet(‘ John ’)方法時,直譯器將處理此函式呼叫,作為類Student將呼叫者(即例項student)和name引數(即‘ John ’)傳送給greet(self, name)函式,該函式列印“Good Morning, John”。

對於感興趣的讀者,這裡有幾件事要知道,可以幫助你更深入地瞭解這個謎。當建立一個Python類時,它宣告的函式就是這個類的屬性(稱為函式物件)。換句話說,類“擁有”這些函式。類的例項不會直接實現這些函式。相反,它們將具有與類中實現的相應函式繫結的相同屬性(即例項方法)。

3。self是一個關鍵詞嗎?

似乎在所有這些已定義的函式中,我們都使用self作為它們的第一個引數。有些人可能錯誤地認為self是Python為這些用例保留的關鍵字。然而,事實並非如此。請看下面一個簡單的例子:

>>> def=5 File “”, line 1 def=5 ^SyntaxError: invalid syntax>>> class=4 File “”, line 1 class=4 ^SyntaxError: invalid syntax>>> self=3

你可能知道,def和class是Python中的關鍵字,我們不能用它們作為變數名。然而,我們可以在定義函式的上下文之外使用self作為變數名,這表明它在Python中不是保留關鍵字。

解開Python中self的四個秘密

4。 我們必須在這些函式宣告中使用self嗎?

在上面的例子中,我們重複引用了greet()函式。正如我們已經討論過的,我們將這個函式實現為一個例項方法,這樣它就可以被這個Student類的所有例項使用。在這種情況下,self是必需的。下面是一些證據:

>>> class Teacher:。。。 def say_hello(name):。。。 print(‘Hello, ’ + name)。。。>>> teacher = Teacher()>>> teacher。say_hello(‘John’)Traceback (most recent call last): File “”, line 1, in TypeError: say_hello() takes 1 positional argument but 2 were given

這裡有一些分析。如前所述,當例項teacher呼叫say_hello()方法時,發生的事情是執行teacher 。say_hello()函式,並將例項物件teacher和‘ John ’設定為函式呼叫。這就是為什麼錯誤說“2是給定的。這與函式的定義相反,函式的定義只有一個引數(name)。

然而,還有兩件事與這個謎有關,你可能想知道:

雖然宣告例項方法需要包含self引數,但它不必命名為self。在這個場景中使用這個名稱只是每個Python程式設計師都能欣賞的一種約定。下面是一個例子,它可以被命名為其他東西而不會引起任何問題。儘管它在語法上是正確的,但不推薦使用,因為它只會讓其他Python程式設計師感到困惑:

>>> class Teacher:。。。 def say_hello(professor, name):。。。 print(‘Hello, ’ + name)。。。>>> teacher = Teacher()>>> teacher。say_hello(‘John’)Hello, John

在宣告其他函式(如類和靜態方法)時,不需要使用self引數。對類和靜態方法的清晰解釋將是以後文章的主題。但我在這裡可以展示的是,當我們宣告一個類方法時,函式確實有一些類似於在例項方法中使用self的東西,它通常被稱為cls,引用類物件本身。它與具體例項無關。下面是一個例子:

>>> class Student:。。。 def __init__(self, name):。。。 self。name = name。。。 @classmethod。。。 def with_names(cls, first_name, last_name):。。。 return cls(first_name + ‘ ’ + last_name)。。。 >>> student = Student。with_names(‘John’, ‘Smith’)>>> student。name‘John Smith’

英文原文:

Unlock the 4 Mysteries of self in Python | by Yong Cui | Better Programming | Medium