Python extendiéndose con - usando super () Python 3 vs Python 2

103

Originalmente quería hacer esta pregunta , pero luego descubrí que ya se había pensado antes ...

Buscando en Google encontré este ejemplo de extensión de configparser . Lo siguiente funciona con 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()

Pero no con 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

Luego leí un poco sobre los estilos Python New Class vs. Old Class (por ejemplo, aquí . Y ahora me pregunto, puedo hacer:

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

Pero, ¿no debería llamar a init? ¿Es esto en Python 2 el equivalente:

 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
fuente
1
En su ejemplo, no necesita definir una __init__()en la subclase si todo lo que hace es llamar a la superclase ' __init__()(en Python 2 o 3); en su lugar, deje que las superclase se hereden.
martineau
Referencia útil: amyboyle.ninja/Python-Inheritance
nu everest
Referencia útil con enlace corregido: amyboyle.ninja/Python-Inheritance
fearless_fool

Respuestas:

155
  • super()(sin argumentos) se introdujo en Python 3 (junto con __class__):

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

    por lo que sería el equivalente de Python 2 para clases de nuevo estilo:

    super(CurrentClass, self)
  • para clases de estilo antiguo siempre puede usar:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
mata
fuente
8
-1. Esta respuesta no me aclaró nada. En Python 2, super(__class__)da NameError: global name '__class__' is not definedy también super(self.__class__)es erróneo. Usted debe proporcionar una instancia como segundo argumento, lo que sugeriría que tiene que hacer super(self.__class__, self), pero eso es incorrecto . Si Class2hereda de Class1y Class1llama super(self.__class__, self).__init__(), Class1's __init__se llamará a sí mismo al crear una instancia de Class2.
jpmc26
Para aclarar un punto, obtengo TypeError: super() takes at least 1 argument (0 given)cuando intento llamar super(self.__class__)en Python 2. (Lo cual no tiene mucho sentido, pero demuestra cuánta información falta en esta respuesta).
jpmc26
3
@ jpmc26: en python2 obtiene este error debido a su tratando de llamar __init__()sin discusión en el súper objeto independiente (que se obtiene llamando super(self.__class__)sólo con un argumento), se necesita un súper objeto dependiente entonces debería funcionar: super(CurrentClass, self).__init__(). No lo use self.__class__porque siempre se referirá a la misma clase al llamar a un padre y, por lo tanto, creará un bucle infinito si ese padre también hace lo mismo.
mata
__class__(miembro) también existe en Python2 .
CristiFati
3
@CristiFati No se trata del __class__miembro, sino del cierre léxico creado implícitamente__class__ que siempre se refiere a la clase que se está definiendo actualmente, que no existe en python2.
mata
48

En un solo caso de herencia (cuando subclasifica una sola clase), su nueva clase hereda métodos de la clase base. Esto incluye __init__. Entonces, si no lo define en su clase, obtendrá el de la base.

Las cosas comienzan a complicarse si introduce herencia múltiple (subclasificando más de una clase a la vez). Esto se debe a que si tiene más de una clase base __init__, su clase heredará solo la primera.

En tales casos, realmente debería usar supersi puede, le explicaré por qué. Pero no siempre se puede. El problema es que todas sus clases base también deben usarlo (y sus clases base también - todo el árbol).

Si ese es el caso, entonces esto también funcionará correctamente (en Python 3, pero podría volver a trabajarlo en Python 2, también lo ha hecho 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

Observe cómo se usan ambas clases base superaunque no tengan sus propias clases base.

Lo que superhace es: llama al método de la siguiente clase en MRO (orden de resolución del método). El MRO Ces: (C, A, B, object). Puedes imprimir C.__mro__para verlo.

Entonces, Chereda __init__de Ay superen A.__init__llamadas B.__init__( Bsigue Aen MRO).

Entonces, al no hacer nada en C, terminas llamando a ambos, que es lo que quieres.

Ahora bien, si no estuvieras usando super, terminarías heredando A.__init__(como antes) pero esta vez no hay nada que te llame B.__init__.

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

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

class C(A, B):
    pass

C()
#prints:
#A

Para arreglar eso tienes que definir C.__init__:

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

El problema con eso es que en árboles MI más complicados, los __init__métodos de algunas clases pueden terminar llamándose más de una vez, mientras que super / MRO garantizan que se llamen solo una vez.

yak
fuente
10
Notice how both base classes use super even though they don't have their own base classes.Ellos tienen. En py3k cada objeto de subclases de clase.
akaRem
Esta es la respuesta que estaba buscando, pero no sabía cómo preguntar. La descripción de MRO es buena.
dturvene
27

En resumen, son equivalentes. Tengamos una vista del historial:

(1) al principio, la función se ve así.

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

(2) para hacer el código más abstracto (y más portátil). Un método común para obtener Super-Class se inventa como:

    super(<class>, <instance>)

Y la función init puede ser:

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

Sin embargo, al requerir una aprobación explícita tanto de la clase como de la instancia, se rompe un poco la regla DRY (Don't Repeat Yourself).

(3) en V3. Es más inteligente

    super()

es suficiente en la mayoría de los casos. Puede consultar http://www.python.org/dev/peps/pep-3135/

wuliang
fuente
22

Solo para tener un ejemplo simple y completo para Python 3, que la mayoría de la gente parece estar usando ahora.

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)

da

42
chickenman
Jonathan Mugan
fuente
3

Otra implementación de python3 que implica el uso de clases abstractas con super (). Deberías recordar eso

super().__init__(name, 10)

tiene el mismo efecto que

Person.__init__(self, name, 10)

Recuerde que hay un 'self' oculto en super (), por lo que el mismo objeto pasa al método init de la superclase y los atributos se agregan al objeto que lo llamó. Por lo tanto, super()se traduce ay Personluego, si incluye el yo oculto, obtiene el fragmento de código anterior.

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
fuente