Python erweitert mit - mit super () Python 3 vs Python 2

103

Ursprünglich wollte ich diese Frage stellen , aber dann stellte ich fest, dass bereits daran gedacht wurde ...

Beim Googeln habe ich dieses Beispiel für die Erweiterung von configparser gefunden . Folgendes funktioniert mit Python 3:

$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
[GCC 4.6.3] on linux2
>>> from configparser import  SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
...     def __init_(self):
...         super().__init__()
... 
>>> cfg = AmritaConfigParser()

Aber nicht mit Python 2:

>>> class AmritaConfigParser(SafeConfigParser):
...       def __init__(self):
...           super(SafeConfigParser).init()
... 
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: must be type, not classob

Dann habe ich ein wenig über Python New Class vs. Old Class-Stile gelesen (z . B. hier . Und jetzt frage ich mich, ob ich Folgendes tun kann:

class MyConfigParser(ConfigParser.ConfigParser):
      def Write(self, fp):
          """override the module's original write funcition"""
          ....
      def MyWrite(self, fp):
          """Define new function and inherit all others"""

Aber sollte ich nicht init anrufen? Ist dies in Python 2 das Äquivalent:

 class AmritaConfigParser(ConfigParser.SafeConfigParser):
    #def __init__(self):
    #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
    #
    # is this the equivalent of the above ? 
    def __init__(self):
        ConfigParser.SafeConfigParser.__init__(self)
Oz123
quelle
1
In Ihrem Beispiel müssen Sie keine __init__()in der Unterklasse definieren, wenn nur die Superklasse ' __init__()(entweder in Python 2 oder 3) aufgerufen wird. Lassen Sie stattdessen einfach die Superklasse vererben.
Martineau
Nützliche Referenz: amyboyle.ninja/Python-Inheritance
nu everest
Nützliche Referenz mit korrigierten Link: amyboyle.ninja/Python-Inheritance
fearless_fool

Antworten:

155
  • super()(ohne Argumente) wurde in Python 3 (zusammen mit __class__) eingeführt:

    super() -> same as super(__class__, self)

    Das wäre also das Python 2-Äquivalent für Klassen neuen Stils:

    super(CurrentClass, self)
  • Für Klassen im alten Stil können Sie immer Folgendes verwenden:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
mata
quelle
8
-1. Diese Antwort hat nichts für mich geklärt. In Python 2 super(__class__)gibt NameError: global name '__class__' is not defined, und super(self.__class__)ist ebenso falsch. Sie müssen eine Instanz als zweites Argument angeben, was darauf hindeutet, dass Sie dies tun müssen super(self.__class__, self), aber das ist falsch . Wenn Class2erbt von Class1und Class1Anrufe super(self.__class__, self).__init__(), Class1‚s __init__wird dann selbst nennen , wenn eine Instanz instanziieren Class2.
jpmc26
Um einen Punkt zu verdeutlichen, erhalte ich TypeError: super() takes at least 1 argument (0 given)beim Versuch, super(self.__class__)Python 2 aufzurufen . (Das macht nicht viel Sinn, zeigt aber, wie viele Informationen in dieser Antwort fehlen.)
jpmc26
3
@ jpmc26: In Python2 wird dieser Fehler angezeigt, da Sie beim Versuch, __init__()das ungebundene Superobjekt ohne Argument aufzurufen (das Sie durch Aufrufen super(self.__class__)mit nur einem Argument erhalten), ein gebundenes Superobjekt benötigen, dann sollte es funktionieren : super(CurrentClass, self).__init__(). Nicht verwenden, self.__class__da sich dies beim Aufrufen eines übergeordneten Elements immer auf dieselbe Klasse bezieht. Erstellen Sie daher eine Endlosschleife, wenn dieses übergeordnete Element dasselbe tut.
Mata
__class__(Mitglied) existiert auch in Python2 .
CristiFati
3
@CristiFati Hier geht es nicht um das __class__Mitglied, sondern um den implizit erstellten lexikalischen __class__Abschluss, der sich immer auf die aktuell definierte Klasse bezieht, die in Python2 nicht vorhanden ist.
Mata
48

In einem einzelnen Vererbungsfall (wenn Sie nur eine Klasse unterordnen) erbt Ihre neue Klasse Methoden der Basisklasse. Dies beinhaltet __init__. Wenn Sie es also nicht in Ihrer Klasse definieren, erhalten Sie das von der Basis.

Die Dinge werden kompliziert, wenn Sie mehrere Vererbungen einführen (Unterklassen von mehr als einer Klasse gleichzeitig). Dies liegt daran __init__, dass Ihre Klasse nur die erste erbt, wenn mehr als eine Basisklasse vorhanden ist .

In solchen Fällen sollten Sie wirklich verwenden, superwenn Sie können, ich werde erklären, warum. Aber das kannst du nicht immer. Das Problem ist, dass alle Ihre Basisklassen es auch verwenden müssen (und auch ihre Basisklassen - den gesamten Baum).

Wenn dies der Fall ist, funktioniert dies auch korrekt (in Python 3, aber Sie können es in Python 2 überarbeiten - es hat auch super):

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

Beachten Sie, wie beide Basisklassen verwendet werden super, obwohl sie keine eigenen Basisklassen haben.

Was superbedeutet, ist: Es ruft die Methode aus der nächsten Klasse in MRO (Method Resolution Order) auf. Der MRO für Cist : (C, A, B, object). Sie können drucken C.__mro__, um es zu sehen.

So Cerbt __init__von Aund superin A.__init__Anrufen B.__init__( Bfolgt Ain MRO).

Wenn Sie also nichts tun C, rufen Sie am Ende beide an, was Sie wollen.

Wenn Sie nicht verwenden würden super, würden Sie am Ende A.__init__(wie zuvor) erben, aber dieses Mal gibt es nichts, was Sie fordern würde B.__init__.

class A:
    def __init__(self):
        print('A')

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()
#prints:
#A

Um dies zu beheben, müssen Sie Folgendes definieren C.__init__:

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

Das Problem dabei ist, dass in komplizierteren MI-Bäumen __init__Methoden einiger Klassen möglicherweise mehrmals aufgerufen werden, während Super / MRO garantieren, dass sie nur einmal aufgerufen werden.

Yak
quelle
10
Notice how both base classes use super even though they don't have their own base classes.Sie haben. In py3k unterklassifiziert jede Klasse das Objekt.
akaRem
Dies ist die Antwort, nach der ich gesucht habe, die ich aber nicht fragen konnte. Die MRO-Beschreibung ist gut.
dturvene
27

Kurz gesagt, sie sind gleichwertig. Lassen Sie uns eine Verlaufsansicht haben:

(1) Die Funktion sieht zunächst so aus.

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2) um Code abstrakter (und portabler) zu machen. Eine übliche Methode, um Super-Class zu erhalten, ist wie folgt erfunden:

    super(<class>, <instance>)

Und die Init-Funktion kann sein:

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

Wenn jedoch sowohl die Klasse als auch die Instanz explizit übergeben werden müssen, wird die DRY-Regel (Don't Repeat Yourself) ein wenig verletzt.

(3) in V3. Es ist schlauer,

    super()

ist in den meisten Fällen genug. Sie können auf http://www.python.org/dev/peps/pep-3135/ verweisen.

Wuliang
quelle
22

Nur um ein einfaches und vollständiges Beispiel für Python 3 zu haben, das die meisten Leute derzeit zu verwenden scheinen.

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)

gibt

42
chickenman
Jonathan Mugan
quelle
3

Eine weitere Python3-Implementierung, die die Verwendung von Abstract-Klassen mit super () beinhaltet. Daran solltest du dich erinnern

super().__init__(name, 10)

hat den gleichen Effekt wie

Person.__init__(self, name, 10)

Denken Sie daran, dass es in super () ein verstecktes 'Selbst' gibt. Das gleiche Objekt wird also an die Superklasse-Init-Methode weitergegeben, und die Attribute werden dem Objekt hinzugefügt, das es aufgerufen hat. Daher super()wird in übersetzt Personund wenn Sie dann das verborgene Selbst einschließen, erhalten Sie den obigen Code frag.

from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
    name = ""
    age = 0

    def __init__(self, personName, personAge):
        self.name = personName
        self.age = personAge

    @abstractmethod
    def showName(self):
        pass

    @abstractmethod
    def showAge(self):
        pass


class Man(Person):

    def __init__(self, name, height):
        self.height = height
        # Person.__init__(self, name, 10)
        super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
        # basically used to call the superclass init . This is used incase you want to call subclass init
        # and then also call superclass's init.
        # Since there's a hidden self in the super's parameters, when it's is called,
        # the superclasses attributes are a part of the same object that was sent out in the super() method

    def showIdentity(self):
        return self.name, self.age, self.height

    def showName(self):
        pass

    def showAge(self):
        pass


a = Man("piyush", "179")
print(a.showIdentity())
SeasonalShot
quelle