I'm mencoba untuk memahami penggunaan super()
. Dari kelihatannya, baik anak-anak kelas dapat dibuat, baik-baik saja.
I'm ingin tahu tentang apa sebenarnya perbedaan antara berikut 2 anak kelas.
class Base(object):
def __init__(self):
print "Base created"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
ChildA()
ChildB()
super()
memungkinkan anda menghindari mengacu pada dasar kelas secara eksplisit, yang dapat menjadi bagus. Tapi keuntungan utama dilengkapi dengan beberapa warisan, di mana segala macam seru dapat terjadi. Lihat standar dokumen di super jika anda belum't sudah.
Perhatikan bahwa sintaks berubah dalam Python 3.0: anda hanya bisa mengatakan super().__init__()
bukan super(Persalinan, mandiri).__init__()
yang IMO lebih sedikit lebih baik. Standar dokumen juga merujuk ke panduan untuk menggunakan super() yang cukup jelas.
I'm mencoba untuk memahami
super()
Alasan kami menggunakan super
ini sehingga anak-anak kelas yang dapat menggunakan koperasi multiple inheritance akan memanggil yang benar berikutnya induk kelas fungsi dalam Metode Resolusi Order (MRO).
Di Python 3, kita bisa menyebutnya seperti ini:
class ChildB(Base):
def __init__(self):
super().__init__()
Di Python versi 2, kita dituntut untuk menggunakan seperti ini:
super(ChildB, self).__init__()
Tanpa super, anda terbatas dalam kemampuan anda untuk menggunakan multiple inheritance:
Base.__init__(self) # Avoid this.
Saya menjelaskan lebih lanjut di bawah ini.
"Apa perbedaan yang ada benar-benar dalam kode ini?:"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
# super().__init__() # you can call super like this in Python 3!
Perbedaan utama dalam kode ini adalah bahwa anda mendapatkan lapisan tipuan dalam __init__
dengan super
, yang menggunakan saat ini untuk menentukan kelas kelas berikutnya's __init__
untuk melihat dalam MRO.
Saya menggambarkan perbedaan ini dalam jawaban di kanonik pertanyaan, Cara menggunakan 'super' di Python?, yang menunjukkan dependency injection dan koperasi multiple inheritance.
super
Berikut ini's kode yang's benar-benar erat setara dengan super
(bagaimana hal itu's diimplementasikan dalam C, minus beberapa pemeriksaan dan mundur perilaku, dan diterjemahkan ke Python):
class ChildB(Base):
def __init__(self):
mro = type(self).mro() # Get the Method Resolution Order.
check_next = mro.index(ChildB) + 1 # Start looking after *this* class.
while check_next < len(mro):
next_class = mro[check_next]
if '__init__' in next_class.__dict__:
next_class.__init__(self)
break
check_next += 1
Menulis sedikit lebih seperti asli Python:
class ChildB(Base):
def __init__(self):
mro = type(self).mro()
for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
if hasattr(next_class, '__init__'):
next_class.__init__(self)
break
Jika kita tidak't memiliki super
objek, kita'd harus menulis manual ini kode di mana-mana (atau menciptakan itu!) untuk memastikan bahwa kita panggil tepat di samping metode dalam Metode Resolusi Order!
Bagaimana super melakukan hal ini di Python 3 tanpa diberitahu secara eksplisit yang kelas dan contoh dari metode itu disebut dari?
Ia mendapat panggilan stack frame, dan menemukan kelas (secara implisit disimpan sebagai variabel bebas, __kelas__
, membuat memanggil fungsi penutupan atas kelas) dan argumen pertama untuk fungsi itu, yang harus menjadi contoh atau kelas yang menginformasikan hal yang Metode Resolusi Order (MRO) untuk digunakan.
Karena hal ini membutuhkan bahwa argumen pertama untuk MRO, menggunakan super
dengan metode statis adalah mustahil.
super() memungkinkan anda menghindari mengacu pada dasar kelas secara eksplisit, yang dapat menjadi bagus. . Tapi keuntungan utama dilengkapi dengan beberapa warisan, di mana segala macam hal-hal menyenangkan yang bisa terjadi. Lihat standar dokumen di super jika anda belum't sudah.
It's lebih tepatnya tangan-wavey dan doesn't memberitahu kita banyak, tapi intinya dari super
ini bukan untuk menghindari penulisan kelas induk. Intinya adalah untuk memastikan bahwa metode berikutnya dalam baris dalam metode resolusi order (MRO) disebut. Hal ini menjadi penting dalam multiple inheritance.
I'll menjelaskan di sini.
class Base(object):
def __init__(self):
print("Base init'ed")
class ChildA(Base):
def __init__(self):
print("ChildA init'ed")
Base.__init__(self)
class ChildB(Base):
def __init__(self):
print("ChildB init'ed")
super(ChildB, self).__init__()
Dan let's menciptakan ketergantungan yang ingin kita dipanggil setelah Anak:
class UserDependency(Base):
def __init__(self):
print("UserDependency init'ed")
super(UserDependency, self).__init__()
Sekarang ingat, Persalinan
menggunakan super, ChildA
tidak:
class UserA(ChildA, UserDependency):
def __init__(self):
print("UserA init'ed")
super(UserA, self).__init__()
class UserB(ChildB, UserDependency):
def __init__(self):
print("UserB init'ed")
super(UserB, self).__init__()
Dan UserA
tidak memanggil UserDependency metode:
>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>
Tapi UserB
, karena Persalinan
menggunakan super
, tidak!:
>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>
Dalam keadaan anda harus melakukan hal berikut, yang lain menjawab menunjukkan, seperti yang anda'll pasti mendapatkan kesalahan ketika anda subclass Persalinan:
super(self.__class__, self).__init__() # Don't do this. Ever.
(Bahwa jawabannya adalah tidak pintar atau sangat menarik, tapi terlepas dari kritik langsung di komentar dan lebih dari 17 downvotes, para penjawab bertahan dalam menunjukkan hal itu sampai semacam editor tetap masalahnya.)
Penjelasan: jawaban Yang disarankan menelepon super seperti ini:
super(self.__class__, self).__init__()
Ini adalah benar salah. super
memungkinkan kita melihat ke depan orang tua dalam MRO (lihat bagian pertama dari jawaban ini) untuk anak-anak kelas. Jika anda mengatakan super
kita're pada anak misalnya's metode, maka akan lookup metode berikutnya dalam baris (mungkin ini salah satu) yang dihasilkan pada rekursi, mungkin menyebabkan kegagalan logis (dalam penjawab's contoh, tidak) atau RuntimeError
ketika rekursi kedalaman terlampaui.
>>> class Polygon(object):
... def __init__(self, id):
... self.id = id
...
>>> class Rectangle(Polygon):
... def __init__(self, id, width, height):
... super(self.__class__, self).__init__(id)
... self.shape = (width, height)
...
>>> class Square(Rectangle):
... pass
...
>>> Square('a', 10, 10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'
It's telah mencatat bahwa di Python 3.0+ anda dapat menggunakan
super().__init__()
untuk membuat panggilan anda, yang lebih ringkas dan tidak memerlukan anda untuk referensi induk ATAU nama kelas secara eksplisit, yang dapat berguna. Saya hanya ingin menambahkan bahwa untuk Python 2.7 atau di bawah, itu adalah mungkin untuk mendapatkan nama-huruf depan oleh tulisan self.__hotel__
bukan nama kelas, yaitu
super(self.__class__, self).__init__()
NAMUN, ini istirahat panggilan untuk super
untuk setiap kelas yang diturunkan dari kelas anda, di mana self.__hotel__
bisa kembali anak-anak kelas. Misalnya:
class Polygon(object):
def __init__(self, id):
self.id = id
class Rectangle(Polygon):
def __init__(self, id, width, height):
super(self.__class__, self).__init__(id)
self.shape = (width, height)
class Square(Rectangle):
pass
Di sini saya memiliki kelas alun-Alun
, yang merupakan sub-class dari Persegi panjang
. Mengatakan aku don't ingin menulis terpisah konstruktor untuk Square
karena konstruktor untuk Persegi panjang
adalah cukup baik, tetapi untuk alasan apapun saya ingin menerapkan Persegi sehingga saya dapat diimplementasikan kembali beberapa metode lain.
Ketika saya membuat Square
menggunakan mSquare = Square('a', 10,10)
, Python panggilan konstruktor untuk Persegi panjang
karena saya belum't diberikan Square
sendiri konstruktor. Namun, dalam konstruktor untuk Persegi panjang
, panggilan super(self.__hotel__,mandiri)
akan kembali superclass dari mSquare
, sehingga panggilan konstruktor untuk Persegi panjang
lagi. Ini adalah bagaimana infinite loop terjadi, sebagaimana disebutkan oleh @S_C. Dalam hal ini, ketika saya menjalankan super(...).__init__()
aku memanggil konstruktor untuk Persegi panjang
tapi sejak saya berikan itu tidak ada argumen, saya akan mendapatkan error.
Hanya kepala sampai... dengan Python 2.7, dan saya percaya sejak super()
diperkenalkan di versi 2.2, anda hanya dapat memanggil super()
jika salah satu orang tua mewarisi dari kelas yang akhirnya mewarisi objek
(gaya baru kelas).
Secara pribadi, seperti untuk python 2.7 kode, saya'm akan terus menggunakan BaseClassName.__init__(self, args)
sampai aku benar-benar mendapatkan keuntungan dari menggunakan super()
.
Ada isn't, benar-benar. super()
terlihat di depan kelas di MRO (metode resolusi order, diakses dengan cls.__mro__
) untuk memanggil metode. Hanya menelepon dasar __init__
panggilan dasar __init__
. Seperti yang terjadi, MRO memiliki tepat satu item-- dasar. Jadi anda're-benar melakukan hal yang sama, tetapi dalam cara yang lebih baik dengan super()
(terutama jika anda masuk ke beberapa warisan nanti).
Perbedaan utama adalah bahwa ChildA.__init__
tanpa syarat akan memanggil Dasar.__init__
sedangkan Persalinan.__init__
akan memanggil __init__
di kelas apa pun yang terjadi menjadi Persalinan
leluhur diri
's garis leluhur
(yang mungkin berbeda dari apa yang anda harapkan).
Jika anda menambahkan Klasik
yang menggunakan multiple inheritance:
class Mixin(Base):
def __init__(self):
print "Mixin stuff"
super(Mixin, self).__init__()
class ChildC(ChildB, Mixin): # Mixin is now between ChildB and Base
pass
ChildC()
help(ChildC) # shows that the the Method Resolution Order is ChildC->ChildB->Mixin->Base
kemudian Base
ini tidak ada lagi orang tua Persalinan
untuk tunjangan anak u
kasus. Sekarang super(Persalinan, mandiri)
akan mengarah ke Mixin
jika diri
adalah tunjangan anak u
misalnya.
Anda telah dimasukkan Mixin
di antara Persalinan
dan Dasar
. Dan anda dapat mengambil keuntungan dari itu dengan super()
Jadi jika anda dirancang kelas anda sehingga mereka dapat digunakan dalam sebuah Koperasi Multiple Inheritance skenario, anda menggunakan super
karena anda don't benar-benar tahu siapa yang akan menjadi nenek moyang pada saat runtime.
The super dianggap super post dan [pycon 2015 menyertai video][2] menjelaskan hal ini dengan cukup baik.