Python estendendo com - usando super () Python 3 vs Python 2

103

Originalmente eu queria fazer essa pergunta , mas descobri que já havia pensado nela antes ...

Procurando no Google, encontrei este exemplo de extensão do configparser . O seguinte funciona com 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()

Mas não com 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

Então eu li um pouco sobre estilos Python New Class vs. Old Class (por exemplo, aqui . E agora estou pensando, posso fazer:

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

Mas, eu não deveria chamar init? Isso em Python 2 é o 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
fonte
1
Em seu exemplo, você não precisa definir um __init__()na subclasse se tudo o que ele faz é chamar a superclasse ' __init__()(no Python 2 ou 3) - em vez disso, apenas deixe os super serem herdados.
martineau
Referência útil: amyboyle.ninja/Python-Inheritance
nu everest
Referência útil com link corrigido: amyboyle.ninja/Python-Inheritance
fearless_fool

Respostas:

155
  • super()(sem argumentos) foi introduzido no Python 3 (junto com __class__):

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

    de modo que seria o equivalente em Python 2 para classes de novo estilo:

    super(CurrentClass, self)
  • para aulas de estilo antigo, você sempre pode usar:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
mata
fonte
8
-1. Essa resposta não esclareceu nada para mim. No Python 2, super(__class__)NameError: global name '__class__' is not definede também super(self.__class__)está errado. Você deve fornecer uma instância como um segundo argumento, o que sugere que você precisa fazer super(self.__class__, self), mas isso está errado . Se Class2herda Class1e Class1chamadas super(self.__class__, self).__init__(), Class1's __init__, em seguida, chamar-se ao instanciar uma instância Class2.
jpmc26
Para esclarecer um ponto, eu entendo TypeError: super() takes at least 1 argument (0 given)quando tento chamar super(self.__class__)Python 2. (O que não faz muito sentido, mas demonstra quanta informação está faltando nesta resposta.)
jpmc26
3
@ jpmc26: em python2 você receber esse erro porque a sua tentativa de chamar __init__()sem discussão sobre o objeto de super não ligado (que você começa chamando super(self.__class__)com apenas um argumento), você precisa de um objeto super-bound, então ele deve funcionar: super(CurrentClass, self).__init__(). Não use self.__class__porque isso sempre fará referência à mesma classe ao chamar um pai e, portanto, criar um loop infinito se esse pai também fizer o mesmo.
mata
__class__(membro) também existe no Python2 .
CristiFati
3
@CristiFati Não se trata do __class__membro, mas sim do fechamento lexical implicitamente criado__class__ que sempre se refere à classe que está sendo definida, que não existe em python2.
mata de
48

Em um único caso de herança (quando você cria uma subclasse de apenas uma classe), sua nova classe herda os métodos da classe base. Isso inclui __init__. Então, se você não definir em sua classe, você obterá o da base.

As coisas começam a ficar complicadas se você introduzir herança múltipla (subclassificação de mais de uma classe por vez). Isso ocorre porque se houver mais de uma classe base __init__, sua classe herdará apenas a primeira.

Nesses casos, você realmente deve usar superse puder, vou explicar por quê. Mas nem sempre você pode. O problema é que todas as suas classes básicas também devem usá-lo (e suas classes básicas também - a árvore inteira).

Se for esse o caso, então isso também funcionará corretamente (no Python 3, mas você pode retrabalhá-lo no Python 2 - ele também funcionou 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 como ambas as classes base usam super, embora não tenham suas próprias classes base.

O que superfaz é: ele chama o método da próxima classe em MRO (ordem de resolução do método). A MRO para Cé: (C, A, B, object). Você pode imprimir C.__mro__para ver.

Portanto, Cherda __init__de Ae superem A.__init__chamadas B.__init__( Bsegue Aem MRO).

Então, ao não fazer nada C, você acaba chamando os dois, que é o que você quer.

Agora, se você não estivesse usando super, acabaria herdando A.__init__(como antes), mas desta vez não há nada que chame B.__init__por você.

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

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

class C(A, B):
    pass

C()
#prints:
#A

Para corrigir isso, você deve definir C.__init__:

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

O problema com isso é que em árvores MI mais complicadas, os __init__métodos de algumas classes podem acabar sendo chamados mais de uma vez, enquanto super / MRO garantem que sejam chamados apenas uma vez.

iaque
fonte
10
Notice how both base classes use super even though they don't have their own base classes.Eles têm. Em py3k, todos os objetos de subclasses de classe.
akaRem
Essa é a resposta que eu procurava, mas não sabia perguntar. A descrição do MRO é boa.
dturvene
27

Em suma, eles são equivalentes. Vamos ver o histórico:

(1) a princípio, a função se parece com isso.

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

(2) para tornar o código mais abstrato (e mais portátil). Um método comum para obter a Superclasse é inventado como:

    super(<class>, <instance>)

E a função init pode ser:

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

No entanto, exigir uma passagem explícita da classe e da instância quebra um pouco a regra DRY (Don't Repeat Yourself).

(3) em V3. É mais inteligente,

    super()

é suficiente na maioria dos casos. Você pode consultar http://www.python.org/dev/peps/pep-3135/

Wuliang
fonte
22

Apenas para ter um exemplo simples e completo para Python 3, que a maioria das pessoas parece estar usando agora.

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
Jonathan Mugan
fonte
3

Outra implementação do python3 que envolve o uso de classes Abstract com super (). Você deve se lembrar disso

super().__init__(name, 10)

tem o mesmo efeito que

Person.__init__(self, name, 10)

Lembre-se de que há um 'self' oculto em super (). Portanto, o mesmo objeto passa para o método init da superclasse e os atributos são adicionados ao objeto que o chamou. Portanto, super()é traduzido para Persone, se você incluir o self oculto, obterá o frag de código acima.

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
fonte