Python'un super () yöntemi birden çok kalıtımla nasıl çalışır?


888

Python nesne yönelimli programlamada oldukça yeniyim ve super()özellikle çoklu kalıtım söz konusu olduğunda işlevi (yeni stil sınıfları) anlamada sorun yaşıyorum .

Örneğin;

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"

Ne alamadım: Third()sınıf her iki yapıcı yöntem miras alacak ? Evet ise, o zaman hangisi super () ile çalıştırılacak ve neden?

Ya diğerini çalıştırmak istersen? Python yöntemi çözünürlük sırası ( MRO ) ile ilgisi olduğunu biliyorum .


Aslında, çoklu kalıtım super()herhangi bir kullanımın olduğu tek durumdur . Ben sadece işe yaramaz yükü, doğrusal kalıtım kullanarak sınıfları ile kullanmanızı tavsiye etmem.
Bachsau

9
@Bachsau, teknik olarak doğrudur, küçük bir ek yüktür, ancak super () daha pitoniktir ve zaman içinde yeniden faktoring ve kodda değişiklik yapılmasına izin verir. Gerçekten adlandırılmış bir sınıfa özgü yönteme ihtiyacınız yoksa super () kullanın.
Paul Whipp

2
Diğer bir problem super()ise, her alt sınıfı da kullanmaya zorlamasıdır, kullanılmadığı zaman super(), alt sınıflama yapan herkes kendine karar verebilir. Kullanan bir geliştirici kullanıldığını bilmiyor super()veya bilmiyorsa, mro ile izlenmesi çok zor olan problemler ortaya çıkabilir.
Bachsau

Hemen hemen her cevabı bir şekilde kafa karıştırıcı buldum. Bunun yerine aslında buraya atıfta bulunursunuz.
matanster

Yanıtlar:


709

Bu, Guido'nun Yöntem yayın emri (önceki iki deneme dahil) blog yazısında makul miktarda ayrıntı ile detaylandırılmıştır .

Örneğinizde Third()arayacaksınız First.__init__. Python, sınıfın ebeveynlerindeki her bir özelliği soldan sağa olarak listelendikçe arar. Bu durumda arıyoruz __init__. Yani, eğer

class Third(First, Second):
    ...

Python bakarak başlayacak Firstve eğer Firstöznitelik yoksa, o zaman bakacaktır Second.

Bu durum, kalıtım yolları kesmeye başladığında (örneğin, Firstmiras alınırsa Second) daha karmaşık hale gelir . Daha fazla ayrıntı için yukarıdaki bağlantıyı okuyun, ancak kısaca Python, alt sınıfın kendisinden başlayarak her sınıfın miras listesinde göründüğü sırayı korumaya çalışacaktır.

Yani, örneğin:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First):
    def __init__(self):
        print "third"

class Fourth(Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print "that's it"

MRO olurdu [Fourth, Second, Third, First].

Bu arada: Python tutarlı bir yöntem çözüm sırası bulamazsa, kullanıcıyı şaşırtacak davranışa geri dönmek yerine bir istisna oluşturur.

Belirsiz bir MRO örneği eklemek için düzenlendi:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        print "third"

Meli Thirdbireyin MRO olmak [First, Second]ya [Second, First]? Açık bir beklenti yok ve Python bir hata ortaya çıkaracak:

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution order (MRO) for bases Second, First

Düzenleme: Yukarıdaki örnekler super()çağrıları eksik olduğunu savunarak birkaç kişi görüyorum , bu yüzden açıklayayım: Örneklerin amacı MRO nasıl inşa olduğunu göstermektir. Onlar edilir değil "ilk \ nsecond \ üçüncü" ya da her neyse yazdırmak için tasarlanmıştır. Elbette örnekle oynayabilir, super()arama ekleyebilir , neler olduğunu görebilir ve Python'un miras modelini daha iyi anlayabilirsiniz. Ama buradaki amacım basit tutmak ve MRO'nun nasıl inşa edildiğini göstermek. Ve açıkladığım gibi inşa edildi:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)

12
Birinci, İkinci ve Üçüncü [ pastebin.com/ezTyZ5Wa ] içinde super () öğesini çağırmaya başladığınızda daha ilginç (ve tartışmalı olarak daha kafa karıştırıcı) hale gelir .
gatoatigrado

52
Birinci sınıflarda süper çağrıların olmayışının bu cevapla ilgili gerçekten büyük bir sorun olduğunu düşünüyorum; bu önemli eleştirel anlayışın nasıl / neden kaybolduğunu tartışmadan.
Sam Hartman

3
Bu cevap basitçe yanlış. Ebeveynlerde süper () çağrılar olmadan hiçbir şey olmaz. @ cansız'ın cevabı doğrudur.
Cerin

8
@Cerin Bu örneğin amacı MRO'nun nasıl inşa edildiğini göstermektir. Bu örnek "birinci \ ikinci saniye" veya daha sonra yazdırmaya yönelik DEĞİLDİR. Ve MRO gerçekten doğrudur: Dördüncü .__ mro__ == (<class ' main .Fourth'>, <class ' main .Second'>, <class ' main .Third'>, <class ' main .First'>, < 'object'> yazın)
rbp

2
Görebildiğim kadarıyla, bu cevap OP'nin sorularından birini kaçırıyor, "Ya diğerini çalıştırmak istiyorsanız?". Bu sorunun cevabını görmek istiyorum. Sadece temel sınıfı adlandırmamız gerekiyor mu?
Ray

251

Kodunuz ve diğer yanıtlar hepsi buggy. super()İlk iki sınıfta, kooperatif alt sınıfının çalışması için gerekli olan çağrıları kaçırıyorlar .

İşte kodun sabit bir sürümü:

class First(object):
    def __init__(self):
        super(First, self).__init__()
        print("first")

class Second(object):
    def __init__(self):
        super(Second, self).__init__()
        print("second")

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print("third")

super()Çağrı aksi yürütme sonunda durur, Birinci ve İkinci de sahip olmak zorunda yüzden her adımda en MRO sonraki yöntem bulur Second.__init__().

Bu ne olsun:

>>> Third()
second
first
third

90
Bu sınıflar kendilerini başlatmak için farklı parametrelere ihtiyaç duyarsa ne yapmalı?
calfzhou

2
"kooperatif alt sınıfı"
Quant Metropolis

6
Bu şekilde, BOTH temel sınıflarının init yöntemleri yürütülürken, orijinal örnek yalnızca MRO'da karşılaşılan ilk init'i çağırır . Sanırım bu, "kooperatif alt sınıflama" terimi tarafından ima ediliyor, ancak bir açıklama yararlı olurdu ('Açık, örtük olmaktan iyidir', bilirsiniz;))
Quant Metropolis

1
Evet, super aracılığıyla çağrılan bir yönteme farklı parametreler geçiriyorsanız, bu yöntemin MRO'yu object () yönüne doğru yükselten tüm uygulamaları uyumlu imzalara sahip olmalıdır. Bu, anahtar kelime parametreleriyle elde edilebilir: yöntemin kullandığı parametrelerden daha fazla parametre kabul edin ve fazlaları yok sayın. Bunu yapmak için genellikle çirkin kabul edilir ve çoğu durumda yeni yöntemler eklemek daha iyidir, ancak init (neredeyse?) Özel bir yöntem adı olarak benzersiz ancak kullanıcı tanımlı parametrelerle.
cansız

15
Python'da çoklu kalıtım tasarımı gerçekten çok kötü. Temel sınıfların neredeyse kimin türeyeceğini ve türetilmiş olan diğer kaç temel sınıfın türetileceğini ve hangi sırayla türeyeceğini bilmeleri gerekir ... aksi supertakdirde ya çalışmaz (parametre uyuşmazlığı nedeniyle), ya da çağırmaz birkaç üs (çünkü superbağlantıyı kesen bir baz yazmadım )!
Nawaz

186

Cevabı biraz cansız bir şekilde ayrıntılandırmak istedim çünkü Python'daki bir çoklu miras hiyerarşisinde super () nasıl kullanılacağını okumaya başladığımda, hemen alamadım.

Ne anlamamız gerekir ki super(MyClass, self).__init__()sağlayan bir sonraki __init__ kullanılan Yöntem Çözünürlük Sipariş (MRO) algoritmasına göre yöntem komple kalıtım hiyerarşisi bağlamında .

Bu son kısmı anlamak çok önemlidir. Örneği tekrar ele alalım:

#!/usr/bin/env python2

class First(object):
  def __init__(self):
    print "First(): entering"
    super(First, self).__init__()
    print "First(): exiting"

class Second(object):
  def __init__(self):
    print "Second(): entering"
    super(Second, self).__init__()
    print "Second(): exiting"

class Third(First, Second):
  def __init__(self):
    print "Third(): entering"
    super(Third, self).__init__()
    print "Third(): exiting"

Guido van Rossum'un Yöntem Çözüm Siparişi hakkındaki bu makaleye göre , çözüm sırası __init__(Python 2.3'ten önce) "derinlik-ilk soldan sağa geçiş" kullanılarak hesaplanmıştır:

Third --> First --> object --> Second --> object

Sonuncusu hariç tüm kopyaları kaldırdıktan sonra şunu elde ederiz:

Third --> First --> Second --> object

Yani, sınıfın bir örneğini somutlaştırdığımızda neler olduğunu takip edelim Third, ör x = Third().

  1. MRO göre Third.__init__yürütür.
    • baskılar Third(): entering
    • daha sonra super(Third, self).__init__()yürütülür ve First.__init__çağrılan MRO döner .
  2. First.__init__ yürütür.
    • baskılar First(): entering
    • daha sonra super(First, self).__init__()yürütülür ve Second.__init__çağrılan MRO döner .
  3. Second.__init__ yürütür.
    • baskılar Second(): entering
    • daha sonra super(Second, self).__init__()yürütülür ve object.__init__çağrılan MRO döner .
  4. object.__init__ yürütür (kodda yazdırma ifadesi yok)
  5. yürütme geri döner ve Second.__init__daha sonra yazdırılırSecond(): exiting
  6. yürütme geri döner ve First.__init__daha sonra yazdırılırFirst(): exiting
  7. yürütme geri döner ve Third.__init__daha sonra yazdırılırThird(): exiting

Bu, Third () öğesinin neden başlatılmasının aşağıdakilere neden olduğunu açıklar:

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting

MRO algoritması, Python 2.3'ten itibaren karmaşık durumlarda iyi çalışacak şekilde geliştirildi, ancak "derinlik-ilk soldan sağa geçiş" + "yinelenenleri sonuncunun beklemesini kaldırmanın" hala çoğu durumda çalıştığını düşünüyorum (lütfen durum böyle değilse yorum yapın). Guido tarafından yazılmış blog yazısı okuduğunuzdan emin olun!


6
İçinde: Hala neden anlamıyorum init İlk super (Birinci, bobin) .__ init __ () çağıran init bu ne MRO dikte çünkü, İkinci!
user389955

@ user389955 Oluşturulan nesne, tüm init yöntemlerine sahip olan Üçüncü türdür. Dolayısıyla, MRO'nun tüm init işlevlerinin belirli bir sırayla bir listesini oluşturduğunu varsayarsanız, her süper çağrıda, sonuna kadar bir adım ileri gidersiniz.
Sreekumar R

15
3. Adımın daha fazla açıklamaya ihtiyacı olduğunu düşünüyorum: Eğer Thirdmiras almadıysa Second, o super(First, self).__init__zaman arayacak object.__init__ve döndükten sonra "ilk" basılacaktır. Ama çünkü Thirdhem devraldığı Firstve Secondyerine çağırmaktan daha object.__init__sonra First.__init__sadece son çağrı bu MRO dikte object.__init__korunur ve baskı tabloların Firstve Secondkadar ulaşamamış olan object.__init__getiri. SecondSon aranandan bu yana , object.__init__içeri Secondgirmeden önce içeri geri döner First.
MountainDrew

1
İlginçtir, pycharm biliyor gibi bütün bu (tanıdığı yüzden onun ipuçları, parametreler Ayrıca girdilerin kovaryans bazı kavramını olan super aramaları. Gitmek hangi konuda konuşmak List[subclass]bir şekilde List[superclass]eğer subclassbir alt sınıfıdır superclass( Listgelir typingPEP 483 modülünün iirc).
Reb.Cabin

Güzel yazı ama yapıcıların argümanları ile ilgili bilgileri özlüyorum, yani İkinci ve İlk farklı argümanlar beklerse ne olur? First'in kurucusu bazı argümanları işlemeli ve gerisini Second'a iletmelidir. Bu doğru mu? First'in Second için gerekli argümanları bilmesi gerektiği doğru gelmiyor.
Christian K.

58

Bu Elmas Sorunu olarak bilinir , sayfanın Python'da bir girişi vardır, ancak kısaca Python üst sınıfın yöntemlerini soldan sağa çağıracaktır.


Bu Elmas Sorunu değil. Elmas Sorunu dört sınıfı içerir ve OP'nin sorusu sadece üçünü içerir.
Ian Goodfellow,

147
objectdördüncü
GP89

28

Bu, başlatma için farklı değişkenler ile birden fazla kalıtım ve aynı işlev çağrısı ile birden fazla MixIns sahip sorunu çözmek için nasıl. Açıkça geçti ** kwargs değişkenleri eklemek ve süper çağrılar için bir uç noktası olarak bir MixIn arabirimi eklemek zorunda kaldı.

İşte Abir uzatılabilir temel sınıf ve Bve Cmixin sınıfları işlevini sağlayan kim her ikisi de f. Ave Bher ikisi de vkendi parametrelerini bekliyor __init__ve Cbekliyor w. Fonksiyon fbir parametre alır y. Qher üç sınıftan da miras alır. MixInFiçin mixin arayüzüdür Bve C.


class A(object):
    def __init__(self, v, *args, **kwargs):
        print "A:init:v[{0}]".format(v)
        kwargs['v']=v
        super(A, self).__init__(*args, **kwargs)
        self.v = v


class MixInF(object):
    def __init__(self, *args, **kwargs):
        print "IObject:init"
    def f(self, y):
        print "IObject:y[{0}]".format(y)


class B(MixInF):
    def __init__(self, v, *args, **kwargs):
        print "B:init:v[{0}]".format(v)
        kwargs['v']=v
        super(B, self).__init__(*args, **kwargs)
        self.v = v
    def f(self, y):
        print "B:f:v[{0}]:y[{1}]".format(self.v, y)
        super(B, self).f(y)


class C(MixInF):
    def __init__(self, w, *args, **kwargs):
        print "C:init:w[{0}]".format(w)
        kwargs['w']=w
        super(C, self).__init__(*args, **kwargs)
        self.w = w
    def f(self, y):
        print "C:f:w[{0}]:y[{1}]".format(self.w, y)
        super(C, self).f(y)


class Q(C,B,A):
    def __init__(self, v, w):
        super(Q, self).__init__(v=v, w=w)
    def f(self, y):
        print "Q:f:y[{0}]".format(y)
        super(Q, self).f(y)

Bence bu belki de ayrı bir soru-cevap olmalı, çünkü MRO kalıtımla işlevler arasında değişen argümanlarla uğraşmadan kendi başına yeterince büyük bir konu (çoklu miras bunun özel bir örneğidir).
cansız

8
Teorik olarak, evet. Pratik olarak, bu senaryo python'da Diamond mirasıyla her karşılaştığımda ortaya çıktı, bu yüzden buraya ekledim. Çünkü elmas kalıtımdan temiz bir şekilde kaçınamadığım yer burası. İşte gelecekteki bazı ekstra bağlantılar: rhettinger.wordpress.com/2011/05/26/super-considered-super code.activestate.com/recipes/…
brent.payne

İstediğimiz anlamsal olarak anlamlı parametre adlarına sahip programlar. Ancak bu örnekte neredeyse tüm parametreler anonim olarak adlandırılmıştır, bu da orijinal programcının kodu belgelemesini ve başka bir programcının kodu okumasını çok daha zorlaştıracaktır.
Arthur

Açıklayıcı isimlerle github
repo'ya bir çekiş

@ brent.payne Bence @Arthur, tüm yaklaşımınızın adlandırılmış parametreler yerine args/ kullanmaya dayanması anlamına geliyordu kwargs.
maksimum

25

Bunun super()soruyu doğrudan yanıtlamadığını anlıyorum , ancak paylaşmak için yeterince alakalı olduğunu düşünüyorum.

Her miras alınan sınıfı doğrudan çağırmanın bir yolu da vardır:


class First(object):
    def __init__(self):
        print '1'

class Second(object):
    def __init__(self):
        print '2'

class Third(First, Second):
    def __init__(self):
        Second.__init__(self)

Bunu bu şekilde yaparsanız, eminim manuel olarak her çağırmak gerekecek Hemen not First'ın __init__()denilen edilmeyecektir.


5
Çağrılmaz çünkü her miras alınan sınıfı çağırmadınız. Sorun varsa ziyade olmasıdır Firstve Secondher ikisi başka bir sınıfa miras ve doğrudan o zaman bu ortak sınıf (elmas Başlangıç noktası) olarak nitelendirdiler olan iki kere denir. süper bundan kaçınıyor.
Trilarion

@Trilarion Evet, yapmayacağından emindim. Ancak, kesinlikle bilmiyordum ve çok olası olmasa da sanki yaptığım gibi söylemek istemedim. Bu objectiki kez çağrılmakla ilgili iyi bir nokta . Bunu düşünmedim. Sadece ebeveyn sınıflarını doğrudan çağırdığınızı belirtmek istedim.
Seaux

Ne yazık ki, init herhangi bir özel yönteme erişmeye çalışırsa bu kırılır :(
Erik Aronesty

21

tüm

Her şeyin aşağı indiğini varsayarsak object(yoksa, kendi başınıza olursunuz), Python sınıf miras ağacınıza dayalı bir yöntem çözümleme sırası (MRO) hesaplar. MRO 3 özelliği karşılar:

  • Sınıftaki çocuklar ebeveynlerinden önce gelir
  • Sol ebeveynler sağ ebeveynlerden önce gelir
  • Bir sınıf MRO'da yalnızca bir kez görünür

Böyle bir sipariş yoksa, Python hataları. Bunun içsel çalışmaları sınıfların soyunun C3 Linerizasyonudur. Bununla ilgili her şeyi buradan okuyun: https://www.python.org/download/releases/2.3/mro/

Dolayısıyla, aşağıdaki örneklerin her ikisinde de:

  1. Çocuk
  2. Ayrıldı
  3. Sağ
  4. ebeveyn

Bir yöntem çağrıldığında, bu yöntemin MRO'daki ilk oluşumu denilen yöntemdir. Bu yöntemi uygulamayan tüm sınıflar atlanır. superBu yöntem içinde yapılan herhangi bir çağrı, MRO'da bu yöntemin bir sonraki tekrarını çağırır. Sonuç olarak, hem miras olarak sınıfları hangi sıraya yerleştirdiğiniz, hem superde yöntemleri yöntemlere çağırdığınız yer önemlidir .

İle superher bir yöntemde ilk

class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print "parent"

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print "left"

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print "right"

class Child(Left, Right):
    def __init__(self):
        super(Child, self).__init__()
        print "child"

Child() Çıktılar:

parent
right
left
child

superHer yöntemde sonuncu ile

class Parent(object):
    def __init__(self):
        print "parent"
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print "left"
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print "right"
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print "child"
        super(Child, self).__init__()

Child() Çıktılar:

child
left
right
parent

Ben erişebileceğiniz görüyoruz Leftkullanarak super()gelen Child. diyelim ki Rightiçeriden erişmek istiyorum Child. Erişmek için bir yol var mı Rightgelen Childsüper kullanılarak? Yoksa doğrudan Rightiçeriden supermi aramalıyım ?
alpha_989

4
@ alpha_989 Yalnızca belirli bir sınıfın yöntemine erişmek istiyorsanız, super yerine doğrudan bu sınıfa başvurmalısınız. Süper, miras zincirini takip etmekle ilgilidir, belirli bir sınıfın yöntemine ulaşmamaktır.
Zags

1
'Bir sınıf MRO'da yalnızca bir kez görünür' ifadesini açıkça belirttiğiniz için teşekkür ederiz. Bu benim sorunumu çözdü. Şimdi nihayet çoklu mirasın nasıl çalıştığını anlıyorum. Birinin MRO'nun özelliklerinden bahsetmesi gerekiyordu!
Tushar Vazirani

18

Hakkında calfzhou yorumuna @ , genellikle, gibi kullanabilirsiniz **kwargs:

Çevrimiçi çalışan örnek

class A(object):
  def __init__(self, a, *args, **kwargs):
    print("A", a)

class B(A):
  def __init__(self, b, *args, **kwargs):
    super(B, self).__init__(*args, **kwargs)
    print("B", b)

class A1(A):
  def __init__(self, a1, *args, **kwargs):
    super(A1, self).__init__(*args, **kwargs)
    print("A1", a1)

class B1(A1, B):
  def __init__(self, b1, *args, **kwargs):
    super(B1, self).__init__(*args, **kwargs)
    print("B1", b1)


B1(a1=6, b1=5, b="hello", a=None)

Sonuç:

A None
B hello
A1 6
B1 5

Bunları konumsal olarak da kullanabilirsiniz:

B1(5, 6, b="hello", a=None)

ama MRO'yu hatırlamanız gerekiyor, bu gerçekten kafa karıştırıcı.

Biraz sinir bozucu olabilirim, ama insanların her seferinde kullanmayı *argsve **kwargsbir yöntemi geçersiz kıldıklarını unuttuklarını fark ettim , bu 'sihirli değişkenlerin' gerçekten kullanışlı ve akılcı kullanımından biri.


Vay canına, bu gerçekten çirkin. Hangi özel üst sınıfı aramak istediğinizi söyleyemeyeceğiniz bir utanç. Yine de, bu bana kompozisyon kullanmak ve veba gibi çoklu kalıtımdan kaçınmak için daha fazla teşvik veriyor.
Tom Busby

15

Henüz kapsanmayan bir diğer nokta da sınıfların başlatılması için parametrelerin geçilmesidir. Hedefinin superalt sınıfa bağlı olması nedeniyle parametreleri iletmenin tek iyi yolu hepsini bir araya getirmektir. Ardından, farklı anlamlarla aynı parametre adına sahip olmadığınıza dikkat edin.

Misal:

class A(object):
    def __init__(self, **kwargs):
        print('A.__init__')
        super().__init__()

class B(A):
    def __init__(self, **kwargs):
        print('B.__init__ {}'.format(kwargs['x']))
        super().__init__(**kwargs)


class C(A):
    def __init__(self, **kwargs):
        print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
        super().__init__(**kwargs)


class D(B, C): # MRO=D, B, C, A
    def __init__(self):
        print('D.__init__')
        super().__init__(a=1, b=2, x=3)

print(D.mro())
D()

verir:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__

Süper sınıfı __init__doğrudan parametrelerin daha doğrudan atamasına superçağırmak caziptir ancak bir süper sınıfta herhangi bir çağrı varsa ve / veya MRO değiştirilirse ve uygulamaya bağlı olarak A sınıfı birden çok kez çağrılabilir.

Sonuç olarak: kooperatif mirası ile başlatma için süper ve spesifik parametreler birlikte iyi çalışmıyor.


5
class First(object):
  def __init__(self, a):
    print "first", a
    super(First, self).__init__(20)

class Second(object):
  def __init__(self, a):
    print "second", a
    super(Second, self).__init__()

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__(10)
    print "that's it"

t = Third()

Çıktı

first 10
second 20
that's it

Call to Third (), Third'te tanımlanan init'i bulur . Ve o rutin içinde super çağrısı First'de tanımlanan init'i çağırır . MRO = [Birinci, İkinci]. Şimdi First'de tanımlanan super init çağrısı, MRO'da aramaya devam edecek ve Second'da tanımlanan init'i bulacaktır ve super'e yapılan herhangi bir çağrı, varsayılan nesne init'i vuracaktır . Umarım bu örnek kavramı açıklığa kavuşturur.

İlk'den süper çağırmazsan. Zincir durur ve aşağıdaki çıktıyı alırsınız.

first 10
that's it

1
çünkü ilk önce, önce 'print' sonra 'super' adını verdiniz.
rocky qi

2
arama emrini göstermek içindi
Seraj Ahmad

4

Learningpythonthehardway'de yanlış değilse super () yerleşik bir işlev olarak adlandırılan bir şey öğreniyorum. Super () işlevini çağırmak, mirasın ebeveyn ve 'kardeşlerden' geçmesine yardımcı olabilir ve daha net görmenize yardımcı olabilir. Hala bir acemiyim ama python2.7'de bu super () kullanma deneyimimi paylaşmayı seviyorum.

Bu sayfadaki yorumları okuduysanız, yazdığınız işlev olan Yöntem Çözünürlük Sırası'nı (MRO) duyacaksınız, MRO aramak ve çalıştırmak için Derinlik-İlk-Soldan Sağa şemasını kullanacaktır. Bununla ilgili daha fazla araştırma yapabilirsiniz.

Super () işlevi ekleyerek

super(First, self).__init__() #example for class First.

Birden fazla örneği ve 'aileleri' super () ile, her birini ve herkesi ekleyerek bağlayabilirsiniz. Ve yöntemleri uygulayacak, bunları geçecek ve kaçırmamaya dikkat edecek! Ancak, onları önce veya sonra eklemek bir fark yaratır Eğer öğrenmepythonthehardway egzersiz 44 yaptıysanız bileceksiniz. Eğlence başlasın !!

Aşağıdaki örneği alarak kopyalayıp yapıştırabilir ve çalıştırmayı deneyebilirsiniz:

class First(object):
    def __init__(self):

        print("first")

class Second(First):
    def __init__(self):
        print("second (before)")
        super(Second, self).__init__()
        print("second (after)")

class Third(First):
    def __init__(self):
        print("third (before)")
        super(Third, self).__init__()
        print("third (after)")


class Fourth(First):
    def __init__(self):
        print("fourth (before)")
        super(Fourth, self).__init__()
        print("fourth (after)")


class Fifth(Second, Third, Fourth):
    def __init__(self):
        print("fifth (before)")
        super(Fifth, self).__init__()
        print("fifth (after)")

Fifth()

Nasıl çalışır? Beşinci () örneği bu şekilde olacaktır. Her adım, süper işlevin eklendiği sınıftan sınıfa gider.

1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)

Ebeveyn bulundu ve Üçüncü ve Dördüncü devam edecek !!

5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)

Şimdi super () ile tüm sınıflara erişildi! Üst sınıf bulundu ve yürütüldü ve şimdi kodları bitirmek için mirastaki işlevin kutusundan çıkarmaya devam ediyor.

9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed

Yukarıdaki programın sonucu:

fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)

Benim için super () ekleyerek python'un kodumu nasıl yürüteceği konusunda daha net görmemi ve kalıtımın istediğim yönteme erişebildiğinden emin olmamı sağlıyor.


Detaylı demo için teşekkürler!
Tushar Vazirani

3

@Visionscaper'ın en üstte ne söylediğine eklemek istiyorum :

Third --> First --> object --> Second --> object

Bu durumda yorumlayıcı, nesne sınıfını çoğalttığı için filtrelemez, bunun yerine İkinci bir kafa konumunda görünür ve bir hiyerarşi alt kümesinde kuyruk konumunda görünmez. Nesne sadece kuyruk pozisyonlarında görünür ve önceliği belirlemek için C3 algoritmasında güçlü bir pozisyon olarak kabul edilmez.

C, L (C) sınıfının doğrusallaştırılması (mro),

  • C Sınıfı
  • artı birleşmesi
    • ebeveynlerinin doğrusallaştırılması P1, P2, .. = L (P1, P2, ...) ve
    • ebeveynlerinin listesi P1, P2, ..

Doğrusallaştırılmış Birleştirme, sipariş önemli olduğundan kuyrukların değil listelerin başı olarak görünen ortak sınıflar seçilerek yapılır (aşağıda açıklığa kavuşacaktır)

Üçüncü Doğrusallaştırma aşağıdaki gibi hesaplanabilir:

    L(O)  := [O]  // the linearization(mro) of O(object), because O has no parents

    L(First)  :=  [First] + merge(L(O), [O])
               =  [First] + merge([O], [O])
               =  [First, O]

    // Similarly, 
    L(Second)  := [Second, O]

    L(Third)   := [Third] + merge(L(First), L(Second), [First, Second])
                = [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2, 
                = [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
                = [Third, First, Second] + merge([O], [O])            
                = [Third, First, Second, O]

Bu nedenle, aşağıdaki kodda bir super () uygulaması için:

class First(object):
  def __init__(self):
    super(First, self).__init__()
    print "first"

class Second(object):
  def __init__(self):
    super(Second, self).__init__()
    print "second"

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__()
    print "that's it"

bu yöntemin nasıl çözüleceği belli oluyor

Third.__init__() ---> First.__init__() ---> Second.__init__() ---> 
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"

"Bunun yerine, Second bir kafa konumunda görünür ve bir hiyerarşi alt kümesinde kuyruk konumunda görünmez." Bir kafa veya kuyruk konumunun ne olduğu, bir hiyerarşi alt kümesinin ne olduğu veya hangi alt kümeden bahsettiğiniz açık değildir.
OrangeSherbet

Kuyruk konumu, sınıf hiyerarşisinde daha yüksek olan veya tam tersi sınıfları ifade eder. Temel sınıf 'nesne' kuyruğun sonundadır. Mro algoritmasını anlamanın anahtarı, 'Second'ın' First'in süper noktası olarak nasıl göründüğüdür. Normalde bunu 'nesne' sınıfı olarak kabul ederdik. Bu doğru, ama sadece 'Birinci' sınıf açısından. Ancak, 'Üçüncü' sınıf perspektifinden bakıldığında, 'İlk' için hiyerarşi sırası farklıdır ve yukarıda gösterildiği gibi hesaplanır. mro algoritması, tüm kalıtsal sınıflar için bu perspektifi (veya hiyerarşi alt kümesini) oluşturmaya çalışır
supi

3

Python'da 3.5+ kalıtım benim için tahmin edilebilir ve çok güzel görünüyor. Lütfen şu koda bakar:

class Base(object):
  def foo(self):
    print("    Base(): entering")
    print("    Base(): exiting")


class First(Base):
  def foo(self):
    print("   First(): entering Will call Second now")
    super().foo()
    print("   First(): exiting")


class Second(Base):
  def foo(self):
    print("  Second(): entering")
    super().foo()
    print("  Second(): exiting")


class Third(First, Second):
  def foo(self):
    print(" Third(): entering")
    super().foo()
    print(" Third(): exiting")


class Fourth(Third):
  def foo(self):
    print("Fourth(): entering")
    super().foo()
    print("Fourth(): exiting")

Fourth().foo()
print(Fourth.__mro__)

Çıktılar:

Fourth(): entering
 Third(): entering
   First(): entering Will call Second now
  Second(): entering
    Base(): entering
    Base(): exiting
  Second(): exiting
   First(): exiting
 Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)

Gördüğünüz gibi, miras alınan her zincir için miras alındığı sırayla tam olarak BİR kez foo çağırır. Bu siparişi arayarak alabilirsiniz . mro :

Dördüncü -> Üçüncü -> Birinci -> İkinci -> Temel -> nesne


2

Belki hala eklenebilecek bir şey var, Django rest_framework ile küçük bir örnek ve dekoratörler. Bu, örtük soruya bir cevap verir: "Neden yine de isteyeyim ki?"

Dediğim gibi: Django rest_framework ile birlikteyiz ve genel görünümler kullanıyoruz ve veritabanımızdaki her bir nesne türü için kendimizi nesne listeleri için GET ve POST sağlayan bir görünüm sınıfı ve GET sağlayan başka bir görünüm sınıfı ile buluyoruz , PUT ve DELETE ayrı nesneler için.

Şimdi POST, PUT ve DELETE Django'nun login_required ile süslemek istiyoruz. Bunun her iki sınıfa da nasıl dokunduğuna dikkat edin, ancak her iki sınıftaki tüm yöntemlere değil.

Bir çözüm birden fazla kalıtımdan geçebilir.

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required

class LoginToPost:
    @method_decorator(login_required)
    def post(self, arg, *args, **kwargs):
        super().post(arg, *args, **kwargs)

Benzer şekilde diğer yöntemler için.

Somut sınıflarımın miras listesinde, LoginToPostönce ListCreateAPIViewve LoginToPutOrDeleteönce eklerdim RetrieveUpdateDestroyAPIView. Benim somut derslerim getsüssüz kalacaktı.


1

Bu cevabı gelecekteki referansım için gönderiyorum.

Python Çoklu Kalıtım bir elmas modeli kullanmalı ve işlev imzası modelde değişmemelidir.

    A
   / \
  B   C
   \ /
    D

Örnek kod snippet'i; -

class A:
    def __init__(self, name=None):
        #  this is the head of the diamond, no need to call super() here
        self.name = name

class B(A):
    def __init__(self, param1='hello', **kwargs):
        super().__init__(**kwargs)
        self.param1 = param1

class C(A):
    def __init__(self, param2='bye', **kwargs):
        super().__init__(**kwargs)
        self.param2 = param2

class D(B, C):
    def __init__(self, works='fine', **kwargs):
        super().__init__(**kwargs)
        print(f"{works=}, {self.param1=}, {self.param2=}, {self.name=}")

d = D(name='Testing')

İşte A sınıfı object


1
Aayrıca çağırıyor olmalı __init__. Ayöntemi "icat etmedi" __init__, bu nedenle başka bir sınıfın AMRO'sunda daha önce olabileceğini varsayamaz. Kimin tek sınıf __init__yöntemi değildir (ve olmamalıdır) çağrısı super().__init__olduğunu object.
chepner

Evet. Bu yüzden A yazdım objectBelki de sanırım, class A (object) : bunun yerine yazmalıyım
Akhil Nadh PC

Aobjectparametresine bir parametre ekliyorsanız olamaz __init__.
chepner
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.