python - with - super() argument 1 must be type, not classobj




Python super() mit__init__()-Methoden verstehen (5)

Diese Frage hat hier bereits eine Antwort:

Ich versuche die Verwendung von super() zu verstehen. So wie es aussieht, können beide Kinderklassen erstellt werden.

Ich bin neugierig auf den tatsächlichen Unterschied zwischen den folgenden 2 Kinderklassen.

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

Ich versuche super() zu verstehen super()

Der Grund für die Verwendung von super ist, dass untergeordnete Klassen, die möglicherweise kooperative Mehrfachvererbung verwenden, die korrekte Funktion der nächsten übergeordneten Klasse in der Method Resolution Order (MRO) aufrufen.

In Python 3 können wir es so nennen:

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

In Python 2 müssen wir es folgendermaßen verwenden:

        super(ChildB, self).__init__()

Ohne Super sind Sie in Ihrer Fähigkeit, Mehrfachvererbung zu verwenden, eingeschränkt:

        Base.__init__(self) # Avoid this.

Ich erkläre weiter unten.

"Welchen Unterschied gibt es eigentlich in diesem Code ?:"

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!

Der Hauptunterschied in diesem Code besteht darin, dass Sie in __init__ mit super eine Indirektionsebene erhalten, die __init__ der aktuellen Klasse ermittelt, __init__ die nächste Klasse __init__ im MRO nachschlagen soll.

Ich illustriere diesen Unterschied in einer Antwort auf die kanonische Frage: Wie benutze ich 'super' in Python? , die Abhängigkeitsinjektion und kooperative Mehrfachvererbung demonstriert.

Wenn Python nicht super

Hier ist Code, der eigentlich sehr gut mit super (wie er in C implementiert ist, abgesehen von einigen Überprüfungs- und Fallback-Verhaltens und in Python übersetzt):

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

Ein bisschen mehr wie native Python geschrieben:

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

Wenn wir das Superobjekt nicht hätten, müssten wir diesen Handbuchcode überall schreiben (oder neu erstellen!), Um sicherzustellen, dass wir die richtige nächste Methode in der Method Resolution Order aufrufen!

Wie macht super das in Python 3, ohne explizit mitzuteilen, von welcher Klasse und Instanz von der Methode, aus der es aufgerufen wurde?

Er ruft den aufrufenden Stack-Frame ab und findet die Klasse (implizit als lokale freie Variable __class__ , die die aufrufende Funktion zu einem Abschluss der Klasse macht) und das erste Argument für diese Funktion, die die Instanz oder Klasse sein sollte, die sie informiert welche Method Resolution Order (MRO) verwendet werden soll.

Da dieses erste Argument für die MRO erforderlich ist, ist die Verwendung von super mit statischen Methoden nicht möglich .

Kritik anderer Antworten:

Mit super () können Sie vermeiden, explizit auf die Basisklasse zu verweisen, was nett sein kann. . Der Hauptvorteil liegt jedoch in der Mehrfachvererbung, bei der alle möglichen lustigen Dinge passieren können. Sehen Sie sich die Standarddokumente auf Super an, wenn Sie dies noch nicht getan haben.

Es ist eher handwinklig und sagt uns nicht viel aus, aber der Sinn von super ist es nicht zu vermeiden, die Elternklasse zu schreiben. Es geht darum sicherzustellen, dass die nächste Methode in der Reihenfolge der Methodenauflösung (MRO) aufgerufen wird. Dies wird bei der Mehrfachvererbung wichtig.

Ich erkläre es hier.

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

Und lassen Sie uns eine Abhängigkeit schaffen, die wir nach dem Kind anrufen wollen:

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

ChildB daran, ChildB verwendet super, ChildA nicht:

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

Und UserA ruft die UserDependency-Methode nicht auf:

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

Aber UserB , weil ChildB super benutzt, tut es !:

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

Kritik für eine andere Antwort

Unter keinen Umständen sollten Sie Folgendes tun, was eine andere Antwort vorschlägt, da Sie bei der Unterklasse ChildB auf jeden Fall Fehler erhalten:

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

(Diese Antwort ist nicht klug oder besonders interessant, aber trotz direkter Kritik in den Kommentaren und über 17 Abstimmungen hat der Beantworter darauf bestanden, es vorzuschlagen, bis ein freundlicher Redakteur sein Problem behoben hat.)

Erklärung: Diese Antwort schlug vor, wie folgt zu rufen:

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

Das ist völlig falsch. super können wir das nächste übergeordnete Element in der MRO (siehe erster Abschnitt dieser Antwort) für untergeordnete Klassen nachschlagen. Wenn Sie super sagen, wir befinden uns in der Methode der untergeordneten Instanz, wird die nächste Methode in der Zeile (wahrscheinlich diese) gesucht, was zu Rekursion führt und wahrscheinlich einen logischen Fehler (im Beispiel des Antworters) oder einen RuntimeError Rekursionstiefe ist überschritten.

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

Der Hauptunterschied besteht darin, dass ChildA.__init__ die Base.__init__ unbedingt Base.__init__ wohingegen ChildB.__init__ __init__ in der Klasse, die zufällig ChildB Vorfahren in der ChildB Vorfahrenlinie ist, ChildB kann (was von den erwarteten abweichen kann).

Wenn Sie ein ClassC hinzufügen, das Mehrfachvererbung verwendet:

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

dann ist Base nicht länger das übergeordnete ChildB von ChildB für ChildC Instanzen. super(ChildB, self) zeigt jetzt auf Mixin wenn self eine ChildC Instanz ist.

Sie haben Mixin zwischen ChildB und Base eingefügt. Und Sie können es mit super()

Wenn Sie Ihre Klassen so konzipiert haben, dass sie in einem Cooperative Multiple Inheritance-Szenario verwendet werden können, verwenden Sie super weil Sie nicht wirklich wissen, wer zur Laufzeit der Vorfahre wird.

Das super in Betracht gezogene Super Post und das pycon 2015 Begleitvideo erklären dies ziemlich gut.


Nur ein Heads-up ... mit Python 2.7, und ich glaube, seit super() in Version 2.2 eingeführt wurde, können Sie super() nur aufrufen, wenn eines der Elternteile von einer Klasse erbt, die schließlich das object erbt ( Klassen im neuen Stil) ).

BaseClassName.__init__(self, args) Python 2.7-Code BaseClassName.__init__(self, args) , werde ich weiterhin BaseClassName.__init__(self, args) bis ich wirklich den Vorteil von super() bekomme.


Super hat keine Nebenwirkungen

Base = ChildB

Base()

funktioniert wie erwartet

Base = ChildA

Base()

kommt in unendliche Rekursion.


super() können Sie vermeiden, explizit auf die Basisklasse zu verweisen, was nett sein kann. Der Hauptvorteil liegt jedoch in der Mehrfachvererbung, bei der alle möglichen lustigen Dinge passieren können. Sehen Sie sich die Standarddokumente auf Super an, wenn Sie dies noch nicht getan haben.

Beachten Sie, dass sich die Syntax in Python 3.0 geändert hat : Sie können einfach super().__init__() anstelle von super(ChildB, self).__init__() wobei IMO um einiges schöner ist. Die Standarddokumente beziehen sich auch auf eine Anleitung zur Verwendung von super (), die ziemlich erklärend ist.







super