Soru __İnit __ () yöntemleri ile Python super () değerini anlama [duplicate]


Bu sorunun zaten bir cevabı var:

Kullanımını anlamaya çalışıyorum super(). Görünüşünden, her iki çocuk sınıfı da oluşturulabilir, sadece iyi.

Aşağıdaki 2 çocuk sınıfı arasındaki gerçek farkı bilmeyi merak ediyorum.

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()

1982
2018-02-23 00:30


Menşei




Cevaplar:


super() Açıkça temel sınıfa başvurmaktan kaçınmanızı sağlar, ki bu hoş olabilir. Fakat asıl avantaj, her çeşitliliğin olduğu birden fazla kalıtımla birlikte gelir. eğlenceli şeyler gerçekleşebilir. Bakın süper standart dokümanlar eğer sen yapmadıysan.

Bunu not et sözdizimi Python 3.0'da değişti: sadece söyleyebilirsin super().__init__() yerine super(ChildB, self).__init__() hangi IMO biraz daha güzel.


1427
2018-02-23 00:37



Bir örnek verebilir misiniz super() argümanlar ile kullanılıyor? - Steven Vascellaro
Açıklayabilir misin super(ChildB, self).__init__() bu ne yapar ChildB ve self süper ile yapmak zorunda - rimiro


Anlamaya çalışıyorum super()

Kullandığımız nedeni super kooperatif çoklu kalıtım kullanan çocuk sınıfları, Yöntem Çözümleme Sırasındaki (MRO) doğru sonraki üst sınıf işlevini çağırır.

Python 3'te, şöyle diyebiliriz:

class ChildB(Base):
    def __init__(self):
        super().__init__() 

Python 2'de bunu şöyle kullanmamız gerekiyor:

        super(ChildB, self).__init__()

Süper olmadan, birden çok devralma kullanma yeteneğiniz sınırlıdır:

        Base.__init__(self) # Avoid this.

Ben aşağıda açıklarım.

"Bu kodda aslında ne fark var?"

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!

Bu koddaki birincil fark, bir __init__ ile superBir sonraki dersin değerini belirlemek için mevcut sınıfı kullanır. __init__ MRO'ya bakmak için.

Bu farkı bir cevapta açıklarım kanonik soru, Python'da 'süper' nasıl kullanılır?, gösterir ki bağımlılık enjeksiyonu ve kooperatif çoklu kalıtım.

Python olmasaydı super

İşte aslında yakından eşdeğer kod super (C'de nasıl uygulanır, bazı kontrol ve geri dönüş davranışları nasıl yapılır ve Python'a çevrilir):

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

Biraz daha yerel Python gibi yazılmıştır:

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

Eğer bizde olmasaydık super Nesne, bu manuel kodu her yerde yazmalıyız (ya da yeniden oluşturmalıyız!) Yöntem Çözümleme Sırasındaki uygun sonraki yöntemi çağırdığımızdan emin olmak için!

Süper, Python 3'te bunu, hangi yöntemden çağrıldığı ve hangi yöntemden çağrıldığı açıkça anlatılmaksızın nasıl yapar?

Çağıran yığın çerçevesini alır ve sınıfı (yerel bir serbest değişken olarak örtük olarak saklanır) bulur. __class__çağrı işlevini, sınıf üzerinde bir kapatma işlevi yapmak ve bu işleve ilişkin ilk bağımsız değişkeni, hangi Yöntem Çözünürlük Sırasının (MRO) kullanılacağını bildiren örnek veya sınıf olması gerekir.

MRO için ilk argümanı gerektirdiğinden, kullanma super statik yöntemlerle imkansız.

Diğer cevapların eleştirisi:

super () taban sınıfına açıkça başvurmaktan kaçınmanızı sağlar, bu da hoş olabilir. . Ancak asıl avantaj, her türlü eğlenceli şeylerin meydana gelebileceği birden fazla kalıtımla birlikte gelir. Henüz yapmadıysanız süper standart belgelere bakın.

Bu oldukça el-dalgalı ve bize çok şey söylemiyor ama super Ebeveyn sınıfını yazmamaktır. Buradaki nokta, yöntem çözünürlük sırasındaki (MRO) sonraki yöntemin çağrıldığından emin olmaktır. Bu, çoklu kalıtımda önemli hale gelir.

Ben burada açıklayacağım.

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__()

Ve çocuktan sonra aranmak istediğimiz bir bağımlılık yaratalım:

class UserDependency(Base):
    def __init__(self):
        print("UserDependency init'ed")
        super(UserDependency, self).__init__()

Şimdi hatırla, ChildB süper kullanır, ChildA değil:

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__()

Ve UserA UserDependency yöntemini çağırmaz:

>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>

Fakat UserB, Çünkü ChildB kullanımları superyapar!

>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>

Başka bir cevap için eleştiri

ChildB alt sınıfını kullandığınızda kesinlikle hatalar alacağınız gibi, başka bir cevabın önerdiği aşağıdakileri yapmanıza gerek yoktur:

        super(self.__class__, self).__init__() # Don't do this. Ever.

(Bu cevap akıllıca ya da özellikle ilginç değildir, fakat yorumlarda ve 17'den fazla notta doğrudan eleştiriye rağmen, cevaplayıcı bir tür editör sorununu çözene kadar bunu önermede ısrar etmiştir.)

Açıklama: Bu cevap şu şekilde süper arama yapılmasını önerdi:

super(self.__class__, self).__init__()

Bu tamamen yanlış. super çocuk sınıfları için MRO'daki bir sonraki ebeveyne bakalım (bu cevabın ilk bölümüne bakınız). Söylersen super Çocuk örneğinin metodundayız, daha sonra bir sonraki yöntemi arayıp (muhtemelen bu bir) özyineleme ile sonuçlanacak, muhtemelen mantıksal bir başarısızlığa neden olacak (cevap verenin örneğinde olduğu gibi) veya bir RuntimeError özyineleme derinliği aşıldığında.

>>> 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'

413
2017-11-25 19:00



Kafamı bu konuda hala çalışmaya ihtiyacım var. super() Ancak, bu cevap, derinlik ve detaylar açısından açıkça en iyisidir. Ayrıca cevabın içerisindeki eleştirileri de çok takdir ediyorum. Ayrıca, diğer cevaplardaki tuzakları tespit ederek kavramın daha iyi anlaşılmasına yardımcı olur. Teşekkür ederim ! - Yohan Obadia
@Aaron Hall, bu kadar detaylı bilgi için teşekkürler. Mentorlar için yeterli ve yeterli bilgi vermedikleri taktirde, bir cevabı uygunsuz veya eksik olarak nitelendirmeleri için bir seçenek daha var (bence). - hunch
Teşekkürler, bu süper yardımcı oldu. Zayıf / uygunsuz kullanımın eleştirisi, neden ve nasıl süper kullanıldığını çok açıklayıcıydı. - Xarses
Python Kalıtımının çok iyi bir açıklaması. Teşekkürler - ioaniatr


Python 3.0 + 'da kullanabileceğiniz belirtildi.

super().__init__() 

Aramanızı yapmak için, bu kısa ve açık olan ve OR sınıf adlarına açıkça başvurmanızı gerektirmez, bu da kullanışlı olabilir. Sadece Python 2.7 veya altında bunu eklemek istiyorum, yazarak bu ad-duyarsız davranışını almak mümkün self.__class__ sınıf adı yerine, yani

super(self.__class__, self).__init__()

NASIL, bu aramayı keser super sınıfınızdan miras alınan tüm sınıflar için self.__class__ bir çocuk dersi verebilirdi. Örneğin:

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

İşte bir sınıfım var Squarebir alt sınıfı olan Rectangle. Ben ayrı bir kurucu yazmak istemediğimi söyle Square çünkü kurucu Rectangle yeterince iyidir, ama neden olursa olsun bir kare uygulamak istiyorum, bu yüzden başka bir yöntemi yeniden yapılandırabilirim.

Ben oluşturduğumda Square kullanma mSquare = Square('a', 10,10)Python, kurucuyu Rectangle çünkü vermedim Square kendi kurucusu. Ancak, yapıcı için Rectangle, arama super(self.__class__,self) superclass dönmek için gidiyor mSquarebu yüzden kurucuyu çağırıyor Rectangle tekrar. @S_C tarafından belirtildiği gibi sonsuz döngü böyle olur. Bu durumda, koştuğumda super(...).__init__() İçin kurucuyu arıyorum Rectangle ama hiçbir argüman vermediğim için bir hata yapacağım.


223
2017-10-08 20:08



Bu cevabın ne anlama geldiğini, super(self.__class__, self).__init__() yeni bir tane sunmadan tekrar sınıflarsanız işe yaramaz __init__. Sonra sonsuz bir tekrarınız var. - glglgl
Bu cevap çok saçma. Bu şekilde süper kötüye gidecekseniz, temel sınıf adını da tekrar kodlayabilirsiniz. Bundan daha az yanlış. Süper ilk argümanın tüm noktası, onun değil Mutlaka kendinin türü. Lütfen rhettinger tarafından "super added super" (ya da videolarından bazılarını izleyin) okuyun. - Veky
Python 2 için burada gösterilen kısayol, daha önce zikredilen tuzaklara sahiptir. Bunu kullanmayın, ya da kodunuz tahmin edemeyeceğiniz şekillerde kırılacak. Bu "kullanışlı kısayol" süper bozulur, ancak hata ayıklama işlemine çok fazla zaman ayırana kadar bunu fark etmeyebilirsiniz. Süper çok fazla ayrıntı varsa, Python 3'ü kullanın. - Ryan Hiebert
Cevabı düzenledim. Maalesef bu düzenleme anlamı 180 derece değiştirirse, ama şimdi bu cevap biraz mantıklı olmalı. - Tino
Hiçbir anlam ifade etmeyen, birisinin yanlış olarak gösterilmiş bir şeyi yapabileceklerini söylemektir. Takma ad kullanabilirsiniz echo için python. Hiç kimse bunu öneremezdi! - Aaron Hall♦


Süper yan etkisi yoktur

Base = ChildB

Base()

beklendiği gibi çalışıyor

Base = ChildA

Base()

sonsuz yineleme içine alır.


75
2017-11-27 23:26



super aramanın hiçbir yan etkisi yoktur, sadece bir proxy nesnesi oluşturur. Bu cevabın ne hakkında konuşmaya çalıştığından emin değilim. super Yine de doğrudan bir ebeveyn sınıfı. - davidism
"Süper'nin yan etkisi yoktur" ifadesi bu bağlamda anlam ifade etmiyor. Super, basit bir şekilde, metod çözümleme sırasındaki doğru sınıfın yöntemini aradığımızı garanti ederken, diğer yol, çağrılacak bir sonraki yöntemin sabit kodlarını koordine eder, bu da kooperatifin çoklu mirasını daha zor hale getirir. - Aaron Hall♦


Python 2.7 ile sadece bir kafa ... ... ve o zamandan beri inanıyorum. super() 2.2 sürümünde tanıtıldı, sadece arayabilirsin super()Ebeveynlerden biri sonunda miras kalan bir sınıftan miras kalırsa object (yeni stil sınıfları).

Şahsen, python 2.7 kodunda olduğu gibi kullanmaya devam edeceğim BaseClassName.__init__(self, args) aslında kullanmanın avantajını elde edene kadar super().


68
2018-05-25 17:52



çok iyi bir nokta. Eğer açıkça belirtmediyseniz: class Base (object): o zaman şöyle bir hata alırsınız: "TypeError: tip olmalı, classobj değil" - andilabs
@andi Geçen gün bu hatayı aldım ve sonunda anlamaya çalışmaktan vazgeçtim. Sadece iPython'da dalga geçiyordum. Ne yazık ki, kod hata ayıklamak zorunda olsaydı ne kötü bir hata mesajı garip bir kabus! - Two-Bit Alchemist


Gerçekten yok. super() MRO'da bir sonraki sınıfa bakar. cls.__mro__) yöntemleri çağırmak. Sadece üssü arıyorum __init__ üssü çağırır __init__. Olduğu gibi, MRO tam olarak bir maddeye sahiptir. Yani gerçekten aynı şeyi yapıyorsun, ama daha güzel bir şekilde super() (özellikle daha sonra birden fazla mirasa girerseniz).


46
2018-02-23 00:34



Anlıyorum. Birden fazla mirasa sahip olan süper () 'in neden daha iyi kullanıldığını biraz açıklayabilir misiniz? Bana göre, base .__ init __ (self) daha kısa (daha temiz). İki temel sınıfım olsaydı, bu satırlardan iki, ya da iki süper () satır olurdu. Ya da "daha güzel" ile ne demek istediğini yanlış anladım mı? - Mizipzor
Aslında bir süper () çizgi olurdu. Birden fazla kalıtımınız olduğunda, MRO hala düzdür. Böylece ilk süper () .__ init__ çağrı bir sonraki sınıfa çağırır. içindeDaha sonra bir sonraki çağrı, vb. Gerçekten bazı dokümanları incelemelisiniz. - Devin Jeanpierre
MRO çocuk sınıfı da nesneyi içerir - bir sınıfın MRO'su görünür mro sınıf değişkeni. - James Brady
Ayrıca, klasik sınıfların (2.2 öncesi) süper desteklemediğini unutmayın - açıkça temel sınıflara başvurmanız gerekir. - James Brady
"MRO çocuk sınıfı da nesneyi içerir - bir sınıfın MRO'su görünür mro sınıf değişkeni "Bu büyük bir oops. - Devin Jeanpierre


Ana fark şu ki ChildA.__init__ kayıtsız şartsız Base.__init__ buna karşılık ChildB.__init__ Arayacağım __init__ içinde sınıf ne olursa olsun ChildB atası selfataları dizisi (beklediğinizden farklı olabilir).

Eklerseniz ClassC çoklu kalıtım kullanır:

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

sonra Base artık ebeveyn değil ChildB için ChildC örnekleri. şimdi super(ChildB, self) işaret edecek Mixin Eğer self bir ChildC örneği.

Eklediniz Mixin arasında ChildB ve Base. Ve bunun avantajından yararlanabilirsiniz. super()

Böylece, sınıflarınızı bir Kooperatif Çoklu Kalıtım senaryosunda kullanılabilecek şekilde tasarlıyorsanız super çünkü aslında çalışma zamanında kimin ata olacağının farkında değilsin.

süper saygın süper mesaj ve pycon 2015 eşlik eden video bunu gayet iyi açıklayın.


22
2017-09-21 06:41



Bu. Anlamı super(ChildB, self) tarafından belirtilen nesnenin MRO'suna bağlı olarak değişir selfçalışma zamanına kadar bilinemez. Başka bir deyişle, yazar ChildB neyi bilmenin bir yolu yok super() onlar garanti edemezse, her durumda çözecektir ChildB asla alt sınıflara ayrılmayacak. - nispio