簡體   English   中英

Python 繼承:何時以及為何 __init__

[英]Python inheritance: when and why __init__

我是 Python 新手,試圖了解繼承方法背后的哲學/邏輯。 問題最終是關於為什么以及何時必須在子類中使用__init__方法。 例子:

似乎從超類繼承的子類不需要有自己的構造函數(__init__ ) 方法。 下面,一只狗繼承了哺乳動物的屬性(名字、年齡)和方法(makenoise)。 您甚至可以添加一個方法 ( do_a_trick ) 看起來一切都“應該”地工作。

但是,如果我想像我在 Cats 類中嘗試做的那樣在子類中添加一個新屬性,我會收到一條錯誤消息,指出“self”未定義。 然而我在 dog 類的定義中使用了“self”。 區別的本質是什么? 它似乎定義了貓,因為我希望我需要使用__init__(self,name)super()__init__(name) 為什么不同?

class Mammals(object):

  def __init__(self,name):
    self.name = name
    print("I am a new-born "+ self.name)  
    self.age = 0

  def makenoise(self):
    print(self.name + " says Hello")

class Dogs(Mammals):

  def do_a_trick(self):
    print(self.name + " can roll over")

class Cats(Mammals):

 self.furry = "True"  #results in error `self' is not defined


mymammal = Mammals("zebra") #output "I am a new-born zebra"
mymammal.makenoise()  #output "zebra says hello"
print(mymmmal.age)    #output 0

mydog = Dogs("family pet") #output "I am a new-born family pet"
mydog.makenoise()  #output "family pet says hello"
print(mydog.age)  # output 0
mydog.do_a_trick() #output "family pet can roll over"

顯式優於隱式。

但是,您可以執行以下操作:

class Dogs(Mammals):
    def __init__(self):
        #add new attribute
        self.someattribute = 'value'
        Mammals.__init__(self)

要么

class Dogs(Mammals):
    def __init__(self):
        #add new attribute
        self.someattribute = 'value'
        super(Mammals, self).__init__()

如果我想像我在 Cats 類中嘗試做的那樣在子類中添加一個新屬性,我會收到一條錯誤消息,指出“self”未定義。 然而我在 dog 類的定義中使用了“self”。

在你的超類 Mammal 中,你有一個__init__函數,它接受一個你選擇*調用self的參數。 當您在__init__函數的主體中時,此參數在范圍內 - 它是一個像任何局部變量一樣的局部變量,並且在包含它的函數終止后無法引用它。 在 Dog 類上定義的函數do_a_trick也接受一個名為self的參數,它也是該函數的本地參數。 使這些變量與眾不同的不是它們的名稱(您可以隨心所欲地稱呼它們),而是事實上,作為 python 中實例方法的第一個參數,它們獲得了對調用它們的對象的引用作為它們的值。 (把最后一句話再讀幾遍,這是理解這一點的關鍵,你可能第一次看不懂。)

現在,在Cat中,您有一行代碼根本不在函數中。 此時范圍內沒有任何內容,包括self ,這就是失敗的原因。 如果您要在Cat中定義一個帶有名為self的參數的函數,則可以引用該參數。 如果該參數恰好是Cat上實例方法的第一個參數,那么它將具有調用它的Cat實例的值。 否則,它會擁有傳遞給它的任何東西。

*你的選擇很明智!

Python 類頂層的聲明成為類屬性。 如果您來自 C++ 或 Java 背景,這類似於聲明靜態成員變量。 您不能在該級別分配實例屬性。

變量self通常指類的一個特定實例,即從中調用方法的實例。 當使用語法inst.method()進行方法調用時,函數的第一個參數是調用該方法的對象inst 在您的情況下,通常按照慣例,該參數在方法的函數體內被命名為self 你可以認為self只是方法體內的一個有效標識符。 您的作業self.furry = True不在方法中進行,因此 self 未在此處定義。

你基本上有兩種選擇來實現你想要的。 首先是將furry正確定義為 cat 類的屬性:

class Cat(Mammals):
    furry = True

    # Rest of Cat implementation ...

或者您可以在 cat 構造函數中設置實例變量furry的值:

class Cat(Mammals):
    def __init__(self):
        super(Mammals, self).__init__(self)
        self.furry = True

    # Rest of Cat implementation ...

如果您正在接觸 Python,我強烈建議您閱讀 Python 文檔的這兩部分:

Python 類

Python數據模型特殊方法(更進階)

正如其他答案所指出的,您在其他函數中看到的self實際上是一個參數。 按照 Python 的慣例,實例方法中的第一個參數始終是self

Cats類從其基類Mammals繼承了__init__函數。 你可以覆蓋__init__ ,你可以調用或不調用基類實現。

如果Cats __init__想要調用基本實現,但不想關心參數,則可以使用 Python 變量參數。 這在以下代碼中顯示。

類聲明:

class Cats(Mammals):

  def __init__(self, *args):
    super().__init__(*args)
    self.furry = "True"

例如,請參閱這個 Stack Overflow 問題,了解有關可變數量參數的星號表示法的一些信息: Can a variable number of arguments be passed to a function?

附加測試代碼:

cat = Cats("cat")
print(vars(cat))

輸出:

I am a new-born cat
{'name': 'cat', 'age': 0, 'furry': 'True'}

您可以通過在構造函數方法即__init__中啟動所有變量來執行 Chankey 的回答中的操作

但是你也可以這樣做

class Cats(Mammals):

 furry = "True"

接着

cat = Cats("Tom")
cat.furry # Returns "True"

不能在函數外使用self的原因是因為self僅明確用於類的實例。 如果你在外面使用它,它會導致歧義。 如果我的回答不清楚,請在評論中告訴我。

__init__方法在創建類實例時運行一次。 因此,如果您想在實例創建時為其設置一個屬性,就在此處進行。 self是一個特殊的關鍵字,作為第一個參數傳遞給每個方法,它指的是實例本身。 __init__在這方面與其他方法沒有什么不同。

“區別的本質是什么”:您定義方法Dog.do_a_trick ,並且像往常一樣接收self作為該方法的參數。 但是在Cat中,你無意中(也許是下意識地!)試圖在類范圍內工作——這就是你如何設置一個類屬性,它的值對所有貓都是相同的:

class Cat(object):
    sound = "meow"

它是不同的,因此您可以同時使用這兩個選項。 有時(不是所有時候,而是偶爾)擁有一個類屬性是很有用的。 所有的貓都有相同的聲音。 但是很多時候您將使用實例屬性——不同的貓有不同的名字; 當你需要時,使用__init__

假設你有一個名為Person的類,它有一個名為get_name的方法,定義如下:

 class Person():
     def __init__(self, first_name, last_name):
         self.first_name = first_name
         self.last_name = last_name

     def get_name(self):
         return self.first_name + ' ' + self.last_name

並且,您將Person的實例創建為p1 現在當你用這個實例調用函數get_name()時,它會在內部轉換

    Person.get_name(p1)

所以,self 就是實例本身。

沒有self你可以將上面的代碼寫成:

 class Person():
     first_name = None
     last_name = None


     def get_name(personobject):
         return personobject.first_name + ' ' + personobject.last_name

我想說的是名稱self只是一個約定。

而對於繼承,如果你想在你的子類中有額外的屬性,你需要先啟動你的超類並根據需要添加你的參數。 例如,如果你想從名為BoyPerson創建一個具有新屬性height的子類,你可以將其定義為:

class Boy(Person):
     def __init__(self, first_name, last_name, height):
         super(Person, self).__init__(first_name, last_name)
         self.height = height

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM