Haruskah __init __ () memanggil kelas induknya __init __ ()?


132

Saya menggunakan itu di Objective-C saya punya konstruk ini:

- (void)init {
    if (self = [super init]) {
        // init class
    }
    return self;
}

Haruskah Python juga memanggil implementasi kelas induk untuk __init__?

class NewClass(SomeOtherClass):
    def __init__(self):
        SomeOtherClass.__init__(self)
        # init class

Apakah ini juga benar / salah untuk __new__()dan __del__()?

Sunting: Ada pertanyaan yang sangat mirip: Inheritance and Overriding __init__in Python


Anda telah mengubah kode Anda secara signifikan. Saya bisa mengerti bahwa aslinya objectadalah kesalahan ketik. Tetapi sekarang Anda bahkan tidak memiliki superjudul yang merujuk pada pertanyaan Anda.
SilentGhost

Saya hanya berpikir bahwa super digunakan sebagai nama untuk kelas induk. Saya tidak berpikir ada orang yang akan memikirkan fungsi ini. Saya minta maaf atas kesalahpahaman.
Georg Schölly

Sebuah mengapa tidak otomatis pertanyaan yang super panggilan: stackoverflow.com/questions/3782827/...
Ciro Santilli郝海东冠状病六四事件法轮功

Jawaban:


67

Dalam Python, memanggil kelas-super ' __init__adalah opsional. Jika Anda menyebutnya, maka juga opsional apakah menggunakan superpengidentifikasi, atau apakah akan secara eksplisit memberi nama kelas super:

object.__init__(self)

Dalam kasus objek, memanggil metode super tidak sepenuhnya diperlukan, karena metode super kosong. Sama untuk __del__.

Di sisi lain, untuk __new__, Anda memang harus memanggil metode super, dan menggunakan kembalinya sebagai objek yang baru dibuat - kecuali jika Anda secara eksplisit ingin mengembalikan sesuatu yang berbeda.


Jadi tidak ada konvensi untuk hanya memanggil implementasi super?
Georg Schölly

5
Di kelas gaya lama, Anda hanya bisa memanggil super init jika kelas super benar-benar memiliki init yang ditentukan (yang seringkali tidak). Oleh karena itu, orang biasanya berpikir tentang memanggil metode super, daripada melakukannya dengan prinsip.
Martin v. Löwis

1
Jika sintaks dalam python sesederhana [super init]itu, itu akan lebih umum. Hanya pemikiran spekulatif; konstruksi super dengan Python 2.x agak canggung bagi saya.
u0b34a0f6ae

Ini sepertinya contoh yang menarik (dan mungkin bertentangan): bytes.com/topic/python/answers/… init
mlvljr

"opsional" karena Anda tidak harus memanggilnya, tetapi jika Anda tidak memanggilnya, itu tidak akan dipanggil secara otomatis.
McKay

140

Jika Anda memerlukan sesuatu dari super __init__untuk dilakukan selain apa yang sedang dilakukan di kelas saat ini, __init__,Anda harus menyebutnya sendiri, karena itu tidak akan terjadi secara otomatis. Tetapi jika Anda tidak membutuhkan apa pun dari super __init__,tidak perlu menyebutnya. Contoh:

>>> class C(object):
        def __init__(self):
            self.b = 1


>>> class D(C):
        def __init__(self):
            super().__init__() # in Python 2 use super(D, self).__init__()
            self.a = 1


>>> class E(C):
        def __init__(self):
            self.a = 1


>>> d = D()
>>> d.a
1
>>> d.b  # This works because of the call to super's init
1
>>> e = E()
>>> e.a
1
>>> e.b  # This is going to fail since nothing in E initializes b...
Traceback (most recent call last):
  File "<pyshell#70>", line 1, in <module>
    e.b  # This is going to fail since nothing in E initializes b...
AttributeError: 'E' object has no attribute 'b'

__del__adalah cara yang sama, (tapi hati-hati mengandalkan __del__untuk finalisasi - pertimbangkan melakukannya melalui dengan pernyataan sebagai gantinya).

Saya jarang menggunakan __new__. saya melakukan semua inisialisasi di__init__.


3
Definisi kelas D (C) harus dikoreksi seperti itusuper(D,self).__init__()
eyquem

11
super () .__ init __ () hanya bekerja di Python 3. Dalam Python 2 Anda membutuhkan super (D, self) .__ init __ ()
Jacinda

"Jika Anda memerlukan sesuatu dari init super ..." - Ini adalah pernyataan yang sangat bermasalah karena itu bukan kasus apakah Anda / subkelas membutuhkan "sesuatu", tetapi apakah kelas dasar membutuhkan sesuatu untuk menjadi valid instance kelas dasar dan bekerja dengan benar. Sebagai pelaksana kelas turunan, internal kelas dasar adalah hal-hal yang Anda tidak bisa / tidak harus tahu, dan bahkan jika Anda melakukannya karena Anda menulis keduanya atau internal didokumentasikan, desain basis dapat berubah di masa depan dan rusak karena ditulis dengan buruk. kelas turunan. Jadi selalu pastikan bahwa kelas dasar diinisialisasi sepenuhnya.
Nick

105

Dalam jawaban Anon:
"Jika Anda perlu sesuatu dari super __init__untuk dilakukan selain apa yang sedang dilakukan di kelas saat ini __init__, Anda harus menyebutnya sendiri, karena itu tidak akan terjadi secara otomatis"

Sungguh luar biasa: dia mengatakan persis kebalikan dari prinsip pewarisan.


Bukannya bahwa "sesuatu dari super __init__ (...) tidak akan terjadi secara otomatis" , itu bahwa itu AKAN terjadi secara otomatis, tetapi itu tidak terjadi karena kelas-dasar ' __init__ditimpa oleh definisi dari turunan-clas__init__

Jadi, MENGAPA mendefinisikan diturunkan_klas ' __init__, karena itu menimpa apa yang ditujukan ketika seseorang resor untuk warisan ??

Itu karena seseorang perlu mendefinisikan sesuatu yang TIDAK dilakukan di kelas dasar ' __init__, dan satu-satunya kemungkinan untuk memperolehnya adalah dengan menempatkan eksekusi dalam __init__fungsi kelas turunan' .
Dengan kata lain, seseorang perlu sesuatu di kelas dasar ' __init__selain apa yang akan secara otomatis dilakukan di kelas dasar' __init__jika yang terakhir ini tidak diganti.
BUKAN sebaliknya.


Kemudian, masalahnya adalah bahwa instruksi yang diinginkan hadir di kelas dasar ' __init__tidak lagi diaktifkan pada saat instantiation. Untuk mengimbangi inaktivasi ini, diperlukan sesuatu yang khusus: memanggil kelas dasar secara eksplisit ' __init__, untuk MENJAGA , BUKAN TAMBAH, inisialisasi dilakukan oleh kelas-dasar' __init__. Itulah tepatnya yang dikatakan dalam dokumen resmi:

Metode utama dalam kelas turunan mungkin sebenarnya ingin memperluas daripada hanya mengganti metode kelas dasar dengan nama yang sama. Ada cara sederhana untuk memanggil metode kelas dasar secara langsung: panggil saja BaseClassName.methodname (mandiri, argumen).
http://docs.python.org/tutorial/classes.html#inheritance

Itu saja ceritanya:

  • ketika tujuannya adalah untuk MENJAGA inisialisasi yang dilakukan oleh kelas-dasar, yaitu pewarisan murni, tidak ada yang istimewa diperlukan, seseorang hanya harus menghindari untuk mendefinisikan suatu __init__fungsi dalam kelas turunan

  • ketika tujuannya adalah untuk MENGGANTI inisialisasi yang dilakukan oleh kelas-dasar, __init__harus didefinisikan dalam kelas turunan

  • ketika tujuannya adalah untuk ADD proses ke inisialisasi yang dilakukan oleh kelas-dasar, kelas turunan ' __init__ harus didefinisikan, terdiri dari panggilan eksplisit ke kelas-dasar__init__


Apa yang saya rasa mencengangkan dalam jabatan Anon bukan hanya bahwa ia mengungkapkan kebalikan dari teori pewarisan, tetapi bahwa ada 5 orang yang lewat yang dipungut suara tanpa mengubah rambut, dan terlebih lagi tidak ada yang bereaksi dalam 2 tahun di utas yang subjek yang menarik harus dibaca relatif sering.


1
Saya yakin posting ini akan dibatalkan. Saya khawatir saya tidak akan punya banyak penjelasan tentang alasannya. Lebih mudah untuk memilih daripada menganalisis teks yang tampaknya tidak dapat dipahami. Saya sudah lama berusaha memahami jabatan Anon sebelum akhirnya saya menyadari bahwa itu ditulis dengan baik dan tidak terlalu otoritatif. Mungkin itu dapat diartikan sebagai kira-kira tepat untuk seseorang yang tahu tentang warisan; tapi saya merasa bingung ketika dibaca oleh seseorang yang memiliki gagasan goyah tentang pewarisan, subjek yang tidak sejelas air batu pada umumnya
eyquem

2
"Saya yakin posting ini akan dibatalkan ..." Masalah utamanya adalah Anda agak terlambat ke pesta, dan kebanyakan orang mungkin tidak membaca melampaui beberapa jawaban pertama. Penjelasan hebat dengan cara +1
Gerrat

Jawaban yang bagus.
Trilarion

3
Anda melewatkan kata-kata penting "sebagai tambahan" dalam kalimat yang Anda kutip dari Aaron. Pernyataan Harun sepenuhnya benar, dan cocok dengan apa yang akhirnya Anda katakan.
GreenAsJade

1
Ini adalah penjelasan pertama yang membuat pilihan desain python masuk akal.
Joseph Garvin

20

Sunting : (setelah perubahan kode)
Tidak ada cara bagi kami untuk memberi tahu Anda apakah Anda perlu atau tidak memanggil orang tua Anda __init__(atau fungsi lainnya). Warisan jelas akan bekerja tanpa panggilan semacam itu. Itu semua tergantung pada logika kode Anda: misalnya, jika semua Anda __init__dilakukan di kelas induk, Anda bisa melewatkan kelas anak __init__sama sekali.

pertimbangkan contoh berikut:

>>> class A:
    def __init__(self, val):
        self.a = val


>>> class B(A):
    pass

>>> class C(A):
    def __init__(self, val):
        A.__init__(self, val)
        self.a += val


>>> A(4).a
4
>>> B(5).a
5
>>> C(6).a
12

Saya menghapus super call dari contoh saya, apakah saya ingin tahu apakah seseorang harus memanggil init class induk atau tidak.
Georg Schölly

Anda mungkin ingin mengedit judul itu. tapi jawaban saya tetap bertahan.
SilentGhost

5

Tidak ada aturan yang keras dan cepat. Dokumentasi untuk kelas harus menunjukkan apakah subclass harus memanggil metode superclass. Kadang-kadang Anda ingin sepenuhnya mengganti perilaku superclass, dan di lain waktu menambahnya - yaitu memanggil kode Anda sendiri sebelum dan / atau setelah panggilan superclass.

Pembaruan: Logika dasar yang sama berlaku untuk panggilan metode apa pun. Konstruktor kadang-kadang membutuhkan pertimbangan khusus (karena mereka sering mengatur keadaan yang menentukan perilaku) dan destruktor karena mereka paralel dengan konstruktor (misalnya dalam alokasi sumber daya, misalnya koneksi basis data). Tetapi hal yang sama mungkin berlaku, misalnya, pada render()metode widget.

Pembaruan lebih lanjut: Apa OPP? Apakah maksud Anda OOP? Tidak - subclass sering perlu tahu sesuatu tentang desain superclass. Bukan detail implementasi internal - tetapi kontrak dasar yang dimiliki superclass dengan kliennya (menggunakan kelas). Ini tidak melanggar prinsip-prinsip OOP dengan cara apa pun. Itu sebabnya protectedadalah konsep yang valid dalam OOP secara umum (meskipun tidak, tentu saja, dengan Python).


Anda mengatakan bahwa kadang-kadang seseorang ingin memanggil kode sendiri sebelum panggilan superclass. Untuk melakukan ini, kita perlu pengetahuan tentang implementasi kelas induk, yang akan melanggar OPP.
Georg Schölly

4

IMO, Anda harus memanggilnya. Jika superclass objectAnda, Anda tidak boleh, tetapi dalam kasus lain saya pikir itu luar biasa untuk tidak menyebutnya. Seperti yang sudah dijawab oleh orang lain, akan sangat nyaman jika kelas Anda bahkan tidak perlu menimpa __init__dirinya sendiri, misalnya ketika tidak memiliki keadaan internal (tambahan) untuk diinisialisasi.


2

Ya, Anda harus selalu memanggil kelas dasar __init__secara eksplisit sebagai praktik pengkodean yang baik. Lupa melakukan ini dapat menyebabkan masalah halus atau menjalankan kesalahan waktu. Ini benar bahkan jika __init__tidak mengambil parameter apa pun. Ini tidak seperti bahasa lain di mana kompiler secara implisit akan memanggil konstruktor kelas dasar untuk Anda. Python tidak melakukan itu!

Alasan utama untuk selalu memanggil kelas dasar _init__adalah bahwa kelas dasar biasanya dapat membuat variabel anggota dan menginisialisasi mereka ke default. Jadi jika Anda tidak memanggil kelas dasar init, tidak ada kode yang akan dieksekusi dan Anda akan berakhir dengan kelas dasar yang tidak memiliki variabel anggota.

Contoh :

class Base:
  def __init__(self):
    print('base init')

class Derived1(Base):
  def __init__(self):
    print('derived1 init')

class Derived2(Base):
  def __init__(self):
    super(Derived2, self).__init__()
    print('derived2 init')

print('Creating Derived1...')
d1 = Derived1()
print('Creating Derived2...')
d2 = Derived2()

Ini mencetak ..

Creating Derived1...
derived1 init
Creating Derived2...
base init
derived2 init

Jalankan kode ini .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.