Python si estende con - usando super () Python 3 vs Python 2


103

All'inizio volevo fare questa domanda , ma poi ho scoperto che era già stato pensato prima ...

Cercando su Google ho trovato questo esempio di estensione del configparser . Quanto segue funziona 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()

Ma non 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

Poi ho letto un po 'sugli stili Python New Class vs. Old Class (ad esempio qui . E ora mi chiedo, posso fare:

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

Ma non dovrei chiamare init? Questo in Python 2 è l'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)

1
Nel tuo esempio non è necessario definire un __init__()nella sottoclasse se tutto ciò che fa è chiamare la super classe ' __init__()(in Python 2 o 3) - invece lascia che i super vengano ereditati.
martineau


Riferimento utile con collegamento corretto: amyboyle.ninja/Python-Inheritance
fearless_fool

Risposte:


155
  • super()(senza argomenti) è stato introdotto in Python 3 (insieme a __class__):

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

    quindi questo sarebbe l'equivalente di Python 2 per le classi di nuovo stile:

    super(CurrentClass, self)
  • per le classi vecchio stile puoi sempre usare:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)

8
-1. Questa risposta non mi ha chiarito nulla. In Python 2, super(__class__)NameError: global name '__class__' is not defineded super(self.__class__)è anch'esso errato. È necessario fornire un'istanza come secondo argomento, il che suggerirebbe di doverlo fare super(self.__class__, self), ma è sbagliato . Se Class2eredita da Class1e Class1chiamate super(self.__class__, self).__init__(), Class1's __init__sarà poi chiamare se stesso quando si crea un'istanza di un'istanza di Class2.
jpmc26

Per chiarire un punto, ottengo TypeError: super() takes at least 1 argument (0 given)quando provo a chiamare super(self.__class__)in Python 2. (Il che non ha molto senso, ma dimostra quante informazioni mancano da questa risposta.)
jpmc26

3
@ jpmc26: in python2 si ottiene questo errore perché il tentativo di chiamare __init__()senza discutere sull'oggetto eccellente non legato (che si ottiene chiamando super(self.__class__)con un solo argomento), è necessario un oggetto super-bound, allora dovrebbe funzionare: super(CurrentClass, self).__init__(). Non usare self.__class__perché si riferirà sempre alla stessa classe quando si chiama un genitore e quindi creerà un ciclo infinito se anche quel genitore fa lo stesso.
mata

__class__(membro) esiste anche in Python2 .
CristiFati

3
@CristiFati Non si tratta del __class__membro ma della chiusura lessicale creata implicitamente__class__ che si riferisce sempre alla classe attualmente in fase di definizione, che non esiste in python2.
mata

48

In un singolo caso di ereditarietà (quando si sottoclasse solo una classe), la nuova classe eredita i metodi della classe base. Questo include __init__. Quindi, se non lo definisci nella tua classe, otterrai quello dalla base.

Le cose iniziano a essere complicate se si introduce l'ereditarietà multipla (sottoclasse più di una classe alla volta). Questo perché se ha più di una classe base __init__, la tua classe erediterà solo la prima.

In questi casi, dovresti davvero usare superse puoi, ti spiego perché. Ma non sempre puoi. Il problema è che anche tutte le classi base devono usarlo (e anche le loro classi base - l'intero albero).

Se è così, allora funzionerà correttamente (in Python 3 ma potresti rielaborarlo in Python 2 - ha anche 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

Si noti come entrambe le classi di base utilizzano superanche se non hanno le proprie classi di base.

Quello che superfa è: chiama il metodo dalla classe successiva in MRO (ordine di risoluzione del metodo). Il MRO per Cè: (C, A, B, object). Puoi stampare C.__mro__per vederlo.

Quindi, Ceredita __init__da Ae supernelle A.__init__chiamate B.__init__( Bsegue Ain MRO).

Quindi, non facendo nulla C, finisci per chiamare entrambi, che è quello che vuoi.

Ora, se non lo usassi super, finiresti per ereditare A.__init__(come prima) ma questa volta non c'è niente che B.__init__ti richiederebbe .

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

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

class C(A, B):
    pass

C()
#prints:
#A

Per risolvere questo problema devi definire C.__init__:

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

Il problema è che in alberi MI più complicati, i __init__metodi di alcune classi possono finire per essere chiamati più di una volta mentre super / MRO garantisce che vengano chiamati solo una volta.


10
Notice how both base classes use super even though they don't have their own base classes.Loro hanno. In py3k ogni oggetto di sottoclassi di classe.
akaRem

Questa è la risposta che stavo cercando, ma non sapevo come chiedere. La descrizione MRO è buona.
dturvene

27

In breve, sono equivalenti. Facciamo una visualizzazione della cronologia:

(1) all'inizio, la funzione è simile a questa.

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

(2) per rendere il codice più astratto (e più portabile). Un metodo comune per ottenere Super-Class è inventato come:

    super(<class>, <instance>)

E la funzione init può essere:

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

Tuttavia, richiedere un passaggio esplicito sia della classe che dell'istanza infrange un po 'la regola DRY (Don't Repeat Yourself).

(3) in V3. È più intelligente,

    super()

è sufficiente nella maggior parte dei casi. Puoi fare riferimento a http://www.python.org/dev/peps/pep-3135/


22

Solo per avere un esempio semplice e completo per Python 3, che la maggior parte delle persone sembra utilizzare ora.

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)

42
chickenman

3

Un'altra implementazione di python3 che prevede l'uso di classi Abstract con super (). Dovresti ricordarlo

super().__init__(name, 10)

ha lo stesso effetto di

Person.__init__(self, name, 10)

Ricorda che c'è un "sé" nascosto in super (), quindi lo stesso oggetto passa al metodo init della superclasse e gli attributi vengono aggiunti all'oggetto che lo ha chiamato. Quindi super()viene tradotto in Persone quindi se includi il sé nascosto, ottieni il frammento di codice sopra.

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())
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.