Saya cukup baru dalam pemrograman berorientasi objek Python dan saya mengalami kesulitan memahami super()
fungsi (kelas gaya baru) terutama ketika datang ke beberapa warisan.
Misalnya jika Anda memiliki sesuatu seperti:
class First(object):
def __init__(self):
print "first"
class Second(object):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
Yang tidak saya dapatkan adalah: apakah Third()
kelas mewarisi kedua metode konstruktor? Jika ya, lalu yang mana yang akan dijalankan dengan super () dan mengapa?
Dan bagaimana jika Anda ingin menjalankan yang lain? Saya tahu ini ada hubungannya dengan metode resolusi urutan Python ( MRO ).
python
multiple-inheritance
Callisto
sumber
sumber
super()
ada gunanya. Saya tidak akan merekomendasikan menggunakannya dengan kelas menggunakan pewarisan linear, di mana itu hanya overhead yang tidak berguna.super()
adalah, bahwa itu memaksa setiap subclass untuk menggunakannya juga, sementara ketika tidak menggunakansuper()
, semua orang subclass itu dapat memutuskan sendiri. Jika pengembang menggunakannya tidak tahusuper()
atau tidak tahu itu digunakan, masalah dengan mro dapat muncul yang sangat sulit dilacak.Jawaban:
Ini dirinci dengan jumlah detail yang masuk akal oleh Guido sendiri dalam posting blognya Metode Resolution Order (termasuk dua upaya sebelumnya).
Dalam contoh Anda,
Third()
akan meneleponFirst.__init__
. Python mencari setiap atribut dalam orangtua kelas karena mereka terdaftar kiri ke kanan. Dalam hal ini, kami sedang mencari__init__
. Jadi, jika Anda mendefinisikanPython akan mulai dengan melihat
First
, dan, jikaFirst
tidak memiliki atribut, maka akan terlihatSecond
.Situasi ini menjadi lebih kompleks ketika warisan mulai melintasi jalur (misalnya jika
First
diwarisi dariSecond
). Bacalah tautan di atas untuk detail lebih lanjut, tetapi, singkatnya, Python akan mencoba mempertahankan urutan di mana setiap kelas muncul di daftar warisan, dimulai dengan kelas anak itu sendiri.Jadi, misalnya, jika Anda punya:
MRO akan menjadi
[Fourth, Second, Third, First].
By the way: jika Python tidak dapat menemukan urutan resolusi metode yang koheren, itu akan menimbulkan pengecualian, bukannya kembali ke perilaku yang mungkin mengejutkan pengguna.
Diedit untuk menambahkan contoh MRO yang ambigu:
Haruskah
Third
MRO menjadi[First, Second]
atau[Second, First]
? Tidak ada harapan yang jelas, dan Python akan meningkatkan kesalahan:Sunting: Saya melihat beberapa orang berpendapat bahwa contoh di atas tidak memiliki
super()
panggilan, jadi izinkan saya menjelaskan: Inti dari contoh adalah untuk menunjukkan bagaimana MRO dibangun. Mereka tidak dimaksudkan untuk mencetak "first \ nsecond \ third" atau apa pun. Anda dapat - dan harus, tentu saja, bermain-main dengan contoh ini, menambahkansuper()
panggilan, melihat apa yang terjadi, dan mendapatkan pemahaman yang lebih dalam tentang model pewarisan Python. Tetapi tujuan saya di sini adalah untuk membuatnya tetap sederhana dan menunjukkan bagaimana MRO dibangun. Dan itu dibangun seperti yang saya jelaskan:sumber
Kode Anda, dan jawaban lainnya, semuanya bermasalah. Mereka kehilangan
super()
panggilan dalam dua kelas pertama yang diperlukan agar subclass koperasi bekerja.Ini adalah versi kode yang sudah diperbaiki:
The
super()
panggilan menemukan metode berikutnya di MRO di setiap langkah, yang mengapa Pertama dan Kedua harus memilikinya juga, jika eksekusi berhenti di akhirSecond.__init__()
.Inilah yang saya dapatkan:
sumber
super
akan gagal dijalankan (karena parameter ketidakcocokan), atau tidak akan memanggil beberapa pangkalan (karena Anda tidak menulissuper
di salah satu pangkalan yang merusak tautan)!Saya ingin menguraikan jawaban dengan sedikit mati karena ketika saya mulai membaca tentang cara menggunakan super () dalam hierarki pewarisan berganda di Python, saya tidak segera mendapatkannya.
Yang perlu Anda pahami adalah
super(MyClass, self).__init__()
menyediakan metode berikutnya__init__
sesuai dengan algoritma Method Resolution Ordering (MRO) yang digunakan dalam konteks hierarki warisan lengkap .Bagian terakhir ini penting untuk dipahami. Mari kita perhatikan contohnya lagi:
Menurut artikel ini tentang Metode Penyelesaian Resolusi oleh Guido van Rossum, perintah untuk menyelesaikan
__init__
dihitung (sebelum Python 2.3) menggunakan "traversal kiri-ke-kanan kedalaman-pertama":Setelah menghapus semua duplikat, kecuali yang terakhir, kami mendapatkan:
Jadi, mari ikuti apa yang terjadi ketika kita membuat instance dari
Third
kelas, misalnyax = Third()
.Third.__init__
dieksekusi.Third(): entering
super(Third, self).__init__()
jalankan dan pengembalian MROFirst.__init__
yang disebut.First.__init__
mengeksekusi.First(): entering
super(First, self).__init__()
jalankan dan pengembalian MROSecond.__init__
yang disebut.Second.__init__
mengeksekusi.Second(): entering
super(Second, self).__init__()
jalankan dan pengembalian MROobject.__init__
yang disebut.object.__init__
dijalankan (tidak ada pernyataan cetak dalam kode di sana)Second.__init__
yang kemudian dicetakSecond(): exiting
First.__init__
yang kemudian dicetakFirst(): exiting
Third.__init__
yang kemudian dicetakThird(): exiting
Ini menjelaskan mengapa instantiating Third () menghasilkan ke:
Algoritma MRO telah ditingkatkan dari Python 2.3 dan seterusnya untuk bekerja dengan baik dalam kasus-kasus yang kompleks, tapi saya rasa menggunakan "kedalaman-pertama-ke-kanan traversal" + "menghapus duplikat harapan untuk yang terakhir" masih berfungsi dalam kebanyakan kasus (silakan berkomentar jika ini bukan masalahnya). Pastikan untuk membaca posting blog oleh Guido!
sumber
Third
tidak mewarisi dariSecond
, makasuper(First, self).__init__
akan memanggilobject.__init__
dan setelah kembali, "pertama" akan dicetak. Tetapi karenaThird
mewarisi dari keduanyaFirst
danSecond
, alih-alih meneleponobject.__init__
setelahFirst.__init__
MRO menentukan bahwa hanya panggilan akhir untukobject.__init__
dipertahankan, dan laporan cetak masukFirst
danSecond
tidak tercapai sampaiobject.__init__
kembali. KarenaSecond
itu yang terakhir meneleponobject.__init__
, ia kembali ke dalamSecond
sebelum kembaliFirst
.List[subclass]
sebagaiList[superclass]
jikasubclass
adalah subkelas darisuperclass
(List
berasal darityping
modul PEP 483 iirc)Ini dikenal sebagai Masalah Berlian , halaman tersebut memiliki entri pada Python, tetapi singkatnya, Python akan memanggil metode superclass dari kiri ke kanan.
sumber
object
adalah yang keempatIni adalah bagaimana saya memecahkan masalah memiliki beberapa pewarisan dengan variabel yang berbeda untuk inisialisasi dan memiliki beberapa MixIns dengan pemanggilan fungsi yang sama. Saya harus secara eksplisit menambahkan variabel untuk lulus ** kwargs dan menambahkan antarmuka MixIn menjadi titik akhir untuk panggilan super.
Berikut
A
adalah kelas dasar yang dapat diperpanjang danB
danC
merupakan kelas MixIn yang menyediakan fungsif
.A
danB
keduanya mengharapkan parameterv
di__init__
danC
mengharapkan merekaw
. Fungsi inif
mengambil satu parametery
.Q
mewarisi dari ketiga kelas.MixInF
adalah antarmuka mixin untukB
danC
.sumber
args
/kwargs
daripada parameter bernama.Saya mengerti ini tidak langsung menjawab
super()
pertanyaan, tetapi saya merasa cukup relevan untuk dibagikan.Ada juga cara untuk memanggil langsung setiap kelas yang diwarisi:
Catatan saja bahwa jika Anda melakukannya dengan cara ini, Anda harus memanggil satu sama secara manual karena saya cukup yakin
First
's__init__()
tidak akan dipanggil.sumber
First
danSecond
keduanya mewarisi kelas lain dan memanggilnya langsung maka kelas umum ini (titik awal berlian) disebut dua kali. Super menghindari ini.object
dipanggil dua kali. Saya tidak memikirkan hal itu. Saya hanya ingin menegaskan bahwa Anda memanggil kelas induk secara langsung.Secara keseluruhan
Dengan asumsi semuanya turun dari
object
(Anda berada di Anda sendiri jika tidak), Python menghitung urutan resolusi metode (MRO) berdasarkan pohon warisan kelas Anda. MRO memenuhi 3 properti:Jika tidak ada pemesanan seperti itu, kesalahan Python. Cara kerja bagian dalam ini adalah Linerisasi C3 dari nenek moyang kelas. Baca semua tentang ini di sini: https://www.python.org/download/releases/2.3/mro/
Jadi, dalam kedua contoh di bawah, itu adalah:
Ketika suatu metode dipanggil, kemunculan pertama metode itu di MRO adalah yang disebut. Setiap kelas yang tidak mengimplementasikan metode itu dilewati. Panggilan apa pun ke
super
dalam metode itu akan memanggil kemunculan metode itu selanjutnya di MRO. Akibatnya, itu penting baik urutan mana Anda menempatkan kelas dalam warisan, dan di mana Anda menempatkan panggilansuper
dalam metode.Dengan
super
pertama di setiap metodeChild()
Output:Dengan yang
super
terakhir di setiap metodeChild()
Output:sumber
Left
menggunakansuper()
dariChild
. misalkan saya ingin mengaksesRight
dari dalamChild
. Apakah ada cara untuk mengaksesRight
dariChild
menggunakan super? Atau haruskah saya langsung meneleponRight
dari dalamsuper
?Tentang @ komentar calfzhou ini , Anda dapat menggunakan, seperti biasanya,
**kwargs
:Contoh menjalankan online
Hasil:
Anda juga dapat menggunakannya secara posisi:
tetapi Anda harus mengingat MRO, itu benar-benar membingungkan.
Saya bisa sedikit mengganggu, tetapi saya perhatikan bahwa orang-orang lupa setiap kali menggunakan
*args
dan**kwargs
ketika mereka mengganti metode, sementara itu salah satu dari sedikit penggunaan yang benar-benar bermanfaat dan waras dari 'variabel ajaib' ini.sumber
Poin lain yang belum dibahas adalah melewati parameter untuk inisialisasi kelas. Karena tujuan dari
super
bergantung pada subkelas, satu-satunya cara yang baik untuk melewatkan parameter adalah mengemas semuanya. Maka berhati-hatilah untuk tidak memiliki nama parameter yang sama dengan makna yang berbeda.Contoh:
memberi:
Memanggil kelas super
__init__
langsung ke penugasan parameter yang lebih langsung memang menggoda tetapi gagal jika adasuper
panggilan di kelas super dan / atau MRO diubah dan kelas A dapat dipanggil beberapa kali, tergantung pada implementasinya.Untuk menyimpulkan: pewarisan kooperatif dan parameter super dan spesifik untuk inisialisasi tidak bekerja bersama dengan sangat baik.
sumber
Output adalah
Panggilan ke Third () menempatkan init yang didefinisikan dalam Third. Dan panggilan ke super dalam rutin itu memanggil init yang didefinisikan dalam First. MRO = [Pertama, Kedua]. Sekarang panggilan ke super in init didefinisikan dalam Pertama akan terus mencari MRO dan menemukan init didefinisikan dalam Kedua, dan setiap panggilan ke super akan menekan objek init default . Saya harap contoh ini menjelaskan konsep ini.
Jika Anda tidak menelepon super dari Pertama. Rantai berhenti dan Anda akan mendapatkan output berikut.
sumber
Dalam learningpythonthehardway saya belajar sesuatu yang disebut super () fungsi bawaan jika tidak salah. Fungsi panggilan super () dapat membantu warisan melewati orang tua dan 'saudara kandung' dan membantu Anda melihat lebih jelas. Saya masih pemula tetapi saya senang berbagi pengalaman saya menggunakan super () ini di python2.7.
Jika Anda telah membaca komentar di halaman ini, Anda akan mendengar tentang Method Resolution Order (MRO), metode yang menjadi fungsi yang Anda tulis, MRO akan menggunakan skema Kedalaman-Pertama-Kiri-ke-Kanan untuk mencari dan menjalankan. Anda dapat melakukan lebih banyak penelitian tentang itu.
Dengan menambahkan fungsi super ()
Anda dapat menghubungkan beberapa instance dan 'keluarga' dengan super (), dengan menambahkan masing-masing dan semua orang di dalamnya. Dan itu akan mengeksekusi metode, pergi melalui mereka dan pastikan Anda tidak ketinggalan! Namun, menambahkannya sebelum atau sesudahnya akan membuat perbedaan, Anda akan tahu jika Anda telah melakukan latihan setelah latihan 44. Biarkan kesenangan dimulai !!
Mengambil contoh di bawah ini, Anda dapat menyalin & menempel dan coba jalankan:
Bagaimana cara kerjanya? Contoh dari kelima () akan seperti ini. Setiap langkah beralih dari kelas ke kelas tempat fungsi super ditambahkan.
Orangtua ditemukan dan akan berlanjut ke Ketiga dan Keempat !!
Sekarang semua kelas dengan super () telah diakses! Kelas induk telah ditemukan dan dieksekusi dan sekarang terus membuka kotak fungsi dalam warisan untuk menyelesaikan kode.
Hasil dari program di atas:
Bagi saya dengan menambahkan super () memungkinkan saya untuk melihat lebih jelas tentang bagaimana python akan mengeksekusi pengkodean saya dan memastikan warisan dapat mengakses metode yang saya maksud.
sumber
Saya ingin menambahkan apa yang dikatakan @Visionscaper di bagian atas:
Dalam hal ini interpreter tidak memfilter kelas objek karena diduplikasi, melainkan karena kedua muncul dalam posisi kepala dan tidak muncul dalam posisi ekor dalam subset hierarki. Sedangkan objek hanya muncul di posisi ekor dan tidak dianggap posisi kuat dalam algoritma C3 untuk menentukan prioritas.
Linearisasi (mro) dari kelas C, L (C), adalah
Penggabungan Linier dilakukan dengan memilih kelas umum yang muncul sebagai kepala daftar dan bukan ekor karena urutan penting (akan menjadi jelas di bawah)
Linearisasi Ketiga dapat dihitung sebagai berikut:
Jadi untuk implementasi super () dalam kode berikut:
menjadi jelas bagaimana metode ini akan diselesaikan
sumber
Dalam pewarisan python 3.5+ terlihat dapat diprediksi dan sangat bagus untuk saya. Silakan lihat kode ini:
Output:
Seperti yang Anda lihat, ia memanggil foo tepat SATU kali untuk setiap rantai yang diwarisi dalam urutan yang sama seperti yang diwarisi. Anda bisa mendapatkan pesanan itu dengan menelepon . mro :
Keempat -> Ketiga -> Pertama -> Kedua -> Basis -> objek
sumber
Mungkin masih ada sesuatu yang bisa ditambahkan, contoh kecil dengan Django rest_framework, dan dekorator. Ini memberikan jawaban untuk pertanyaan tersirat: "mengapa saya tetap menginginkan ini?"
Seperti yang dikatakan: kami menggunakan Django rest_framework, dan kami menggunakan tampilan umum, dan untuk setiap jenis objek dalam basis data kami, kami mendapati diri kami dengan satu kelas tampilan yang menyediakan GET dan POST untuk daftar objek, dan kelas tampilan lainnya menyediakan GET , PUT, dan DELETE untuk objek individual.
Sekarang POST, PUT, dan DELETE kami ingin menghiasnya dengan login_required Django. Perhatikan bagaimana ini menyentuh kedua kelas, tetapi tidak semua metode di kedua kelas.
Sebuah solusi bisa melalui banyak pewarisan.
Demikian juga untuk metode lainnya.
Dalam daftar warisan kelas konkret saya, saya akan menambahkan
LoginToPost
sebelumListCreateAPIView
danLoginToPutOrDelete
sebelumnyaRetrieveUpdateDestroyAPIView
. Kelas konkret sayaget
akan tetap tidak didekorasi.sumber
Posting jawaban ini untuk referensi saya di masa mendatang.
Python Multiple Inheritance harus menggunakan model berlian dan tanda tangan fungsi tidak boleh berubah dalam model.
Cuplikan kode contoh adalah; -
Di sini kelas A adalah
object
sumber
A
harus juga menjadi menelepon__init__
.A
tidak "menemukan" metode ini__init__
, sehingga tidak dapat berasumsi bahwa beberapa kelas lain mungkin memilikiA
sebelumnya dalam MRO-nya. Satu-satunya kelas yang__init__
metodenya tidak (dan tidak seharusnya) meneleponsuper().__init__
adalahobject
.object
Mungkin saya pikir, saya harus menulisclass A (object) :
sebagai gantinyaA
tidak bisaobject
jika Anda menambahkan parameter ke parameternya__init__
.