Extension de Python avec - en utilisant super () Python 3 vs Python 2

103

Au départ je voulais poser cette question , mais ensuite j'ai trouvé qu'on y avait déjà pensé avant ...

En cherchant sur Google, j'ai trouvé cet exemple d' extension de configparser . Ce qui suit fonctionne avec 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()

Mais pas avec 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

Ensuite, j'ai lu un peu sur les styles Python New Class vs Old Class (par exemple ici . Et maintenant je me demande, je peux faire:

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

Mais, ne devrais-je pas appeler init? Est-ce que c'est en Python 2 l'é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
la source
1
Dans votre exemple, vous n'avez pas besoin de définir un __init__()dans la sous-classe si tout ce qu'il fait est d'appeler la super classe ' __init__()(en Python 2 ou 3) - au lieu de cela, laissez simplement les super être hérités.
martineau
Référence utile: amyboyle.ninja/Python-Inheritance
nu everest
Référence utile avec lien corrigé: amyboyle.ninja/Python-Inheritance
fearless_fool

Réponses:

155
  • super()(sans arguments) a été introduit dans Python 3 (avec __class__):

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

    ce serait donc l'équivalent Python 2 pour les classes de style nouveau:

    super(CurrentClass, self)
  • pour les classes à l'ancienne, vous pouvez toujours utiliser:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
mata
la source
8
-1. Cette réponse n'a rien clarifié pour moi. En Python 2, super(__class__)donne NameError: global name '__class__' is not definedet super(self.__class__)est également erroné. Vous devez fournir une instance comme deuxième argument, ce qui suggère que vous devez le faire super(self.__class__, self), mais c'est faux . Si Class2hérite de Class1et Class1appels super(self.__class__, self).__init__(), Class1est __init__sera alors appeler lui - même lors de l' instanciation d' une instance de Class2.
jpmc26
Pour clarifier un point, j'obtiens TypeError: super() takes at least 1 argument (0 given)en essayant d'appeler super(self.__class__)Python 2. (Ce qui n'a pas beaucoup de sens, mais cela montre combien d'informations manquent à cette réponse.)
jpmc26
3
@ jpmc26: en python2 vous obtenez cette erreur parce que vous essayez d'appeler __init__()sans argument l'objet super non liée (que vous obtenez en appelant super(self.__class__)avec un seul argument), vous avez besoin d' un objet super lié alors il devrait fonctionner: super(CurrentClass, self).__init__(). Ne l'utilisez pas self.__class__car cela fera toujours référence à la même classe lors de l'appel d'un parent et donc créera une boucle infinie si ce parent fait également la même chose.
mata
__class__(membre) existe également en Python2 .
CristiFati
3
@CristiFati Il ne s'agit pas du __class__membre mais de la fermeture lexicale implicitement créée__class__ qui fait toujours référence à la classe en cours de définition, qui n'existe pas dans python2.
mata
48

Dans un cas d'héritage unique (lorsque vous sous-classez une seule classe), votre nouvelle classe hérite des méthodes de la classe de base. Cela comprend __init__. Donc, si vous ne le définissez pas dans votre classe, vous obtiendrez celui de la base.

Les choses commencent à être compliquées si vous introduisez l'héritage multiple (sous-classant plus d'une classe à la fois). En effet, si plus d'une classe de base en a __init__, votre classe héritera de la première uniquement.

Dans de tels cas, vous devriez vraiment utiliser supersi vous le pouvez, je vais vous expliquer pourquoi. Mais pas toujours vous le pouvez. Le problème est que toutes vos classes de base doivent également l'utiliser (ainsi que leurs classes de base - tout l'arborescence).

Si tel est le cas, cela fonctionnera également correctement (en Python 3 mais vous pouvez le retravailler en Python 2 - il a également 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

Remarquez comment les deux classes de base utilisent supermême si elles n'ont pas leurs propres classes de base.

Qu'est super- ce que c'est: il appelle la méthode de la classe suivante dans MRO (ordre de résolution des méthodes). Le MRO pour Cest: (C, A, B, object). Vous pouvez imprimer C.__mro__pour le voir.

Donc, Chérite __init__de Aet superdans les A.__init__appels B.__init__( Bsuit Adans MRO).

Donc, en ne faisant rien C, vous finissez par appeler les deux, ce que vous voulez.

Maintenant, si vous n'utilisiez pas super, vous finiriez par hériter A.__init__(comme avant) mais cette fois, il n'y a rien qui puisse vous appeler B.__init__.

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

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

class C(A, B):
    pass

C()
#prints:
#A

Pour résoudre ce problème, vous devez définir C.__init__:

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

Le problème avec cela est que dans les arbres MI plus compliqués, les __init__méthodes de certaines classes peuvent finir par être appelées plus d'une fois alors que super / MRO garantit qu'elles ne sont appelées qu'une seule fois.

yak
la source
10
Notice how both base classes use super even though they don't have their own base classes.Ils ont. Dans py3k, chaque classe sous-classe un objet.
akaRem
C'est la réponse que je cherchais, mais je ne savais pas comment demander. La description MRO est bonne.
dturvene
27

Bref, ils sont équivalents. Ayons une vue historique:

(1) au début, la fonction ressemble à ceci.

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

(2) pour rendre le code plus abstrait (et plus portable). Une méthode courante pour obtenir Super-Class est inventée comme:

    super(<class>, <instance>)

Et la fonction init peut être:

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

Cependant, exiger un passage explicite de la classe et de l'instance enfreint un peu la règle DRY (Don't Repeat Yourself).

(3) en V3. C'est plus intelligent,

    super()

est suffisant dans la plupart des cas. Vous pouvez vous référer à http://www.python.org/dev/peps/pep-3135/

wuliang
la source
22

Juste pour avoir un exemple simple et complet pour Python 3, que la plupart des gens semblent utiliser maintenant.

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)

donne

42
chickenman
Jonathan Mugan
la source
3

Une autre implémentation de python3 qui implique l'utilisation de classes abstraites avec super (). Tu devrais te rappeler que

super().__init__(name, 10)

a le même effet que

Person.__init__(self, name, 10)

Souvenez-vous qu'il y a un 'self' caché dans super (), donc le même objet passe à la méthode init de la superclasse et les attributs sont ajoutés à l'objet qui l'a appelé. Par conséquent, il super()est traduit en Personet si vous incluez le soi caché, vous obtenez le fragment de code ci-dessus.

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())
Saisonnier
la source