Qual é a diferença entre:
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
e:
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
Vi super
ser usado bastante em classes com apenas uma herança. Percebo por que você o usaria em herança múltipla, mas não estou claro quanto às vantagens de usá-lo nesse tipo de situação.
python
oop
inheritance
super
user25785
fonte
fonte
Qual é a diferença?
significa chamar
SomeBaseClass
's__init__
. enquantosignifica chamar um limite
__init__
da classe pai que segueChild
no Method Resolution Order (MRO) da instância.Se a instância for uma subclasse de Child, pode haver um pai diferente que vem a seguir no MRO.
Explicado simplesmente
Quando você escreve uma classe, deseja que outras classes possam usá-la.
super()
facilita que outras classes usem a classe que você está escrevendo.Como diz Bob Martin, uma boa arquitetura permite que você adie a tomada de decisões pelo maior tempo possível.
super()
pode ativar esse tipo de arquitetura.Quando outra classe subclasses a classe que você escreveu, também pode ser herdada de outras classes. E essas classes podem ter um
__init__
que vem depois disso com__init__
base na ordem das classes para a resolução do método.Sem
super
você, provavelmente, codificaria o pai da classe que você está escrevendo (como o exemplo). Isso significaria que você não chamaria o próximo__init__
no MRO e, portanto, não conseguiria reutilizar o código nele.Se você estiver escrevendo seu próprio código para uso pessoal, talvez não se importe com essa distinção. Mas se você quiser que outras pessoas usem seu código, usar
super
é algo que permite maior flexibilidade para os usuários do código.Python 2 versus 3
Isso funciona no Python 2 e 3:
Isso funciona apenas no Python 3:
Ele funciona sem argumentos, movendo-se no quadro da pilha e obtendo o primeiro argumento para o método (geralmente
self
para um método de instância oucls
para um método de classe - mas pode haver outros nomes) e localizando a classe (por exemploChild
) nas variáveis livres ( ele é procurado com o nome__class__
como uma variável de fechamento livre no método).Prefiro demonstrar a maneira compatível de usar
super
, mas se você estiver usando apenas o Python 3, poderá chamá-lo sem argumentos.Indirecionamento com compatibilidade direta
O que isso te dá? Para herança única, os exemplos da pergunta são praticamente idênticos do ponto de vista da análise estática. No entanto, usar
super
fornece uma camada de indireção com compatibilidade direta.A compatibilidade direta é muito importante para desenvolvedores experientes. Você deseja que seu código continue trabalhando com alterações mínimas à medida que o altera. Quando você olha para o seu histórico de revisões, deseja ver exatamente o que mudou quando.
Você pode começar com uma herança única, mas se decidir adicionar outra classe base, precisará alterar apenas a linha com as bases - se as bases mudarem em uma classe da qual você herda (digamos que um mixin seja adicionado), você mudaria nada nesta aula. Particularmente no Python 2, obter os argumentos
super
e os argumentos corretos do método pode ser difícil. Se você sabe que está usandosuper
corretamente com herança única, isso torna a depuração menos difícil daqui para frente.Injeção de dependência
Outras pessoas podem usar seu código e injetar os pais na resolução do método:
Digamos que você adicione outra classe ao seu objeto e queira injetar uma classe entre Foo e Bar (para teste ou por outro motivo):
O uso do filho não-super falha ao injetar a dependência, porque o filho que você está usando codificou o método a ser chamado após o seu:
No entanto, a classe com o filho que usa
super
pode injetar corretamente a dependência:Endereçando um comentário
Python lineariza uma árvore de herança complicada por meio do algoritmo de linearização C3 para criar uma Ordem de Resolução de Método (MRO).
Queremos que os métodos sejam pesquisados nessa ordem .
Para que um método definido em um pai encontre o próximo nessa ordem sem
super
, ele teria queO
UnsuperChild
que não tem acesso aInjectMe
. É oUnsuperInjector
que tem acessoInjectMe
- e ainda não pode chamar o método dessa classe do método que herdaUnsuperChild
.As duas classes filho pretendem chamar um método com o mesmo nome que vem a seguir no MRO, que pode ser outra classe da qual não estava ciente quando foi criada.
Aquele que não
super
codifica o método de seu pai - portanto, restringiu o comportamento de seu método e as subclasses não podem injetar funcionalidade na cadeia de chamadas.Aquele com
super
maior flexibilidade. A cadeia de chamadas dos métodos pode ser interceptada e a funcionalidade injetada.Você pode não precisar dessa funcionalidade, mas as subclassers do seu código podem.
Conclusão
Sempre use
super
para referenciar a classe pai em vez de codificá-la.O que você pretende é fazer referência à classe pai que é a próxima na linha, e não especificamente a que você vê o filho herdar.
O não uso
super
pode colocar restrições desnecessárias aos usuários do seu código.fonte
list
interface, digamosdoublylinkedlist
que o aplicativo a escolha com facilidade. Posso tornar meu exemplo mais configurável, introduzindoconfig.txt
e vinculando a implementação no tempo de carregamento. Esse é o exemplo certo? Se sim, como relaciono seu código? Veja o primeiro adv do DI no wiki. Onde está configurável qualquer nova implementação? no seu códigoInjectMe
classe. Os comentários não são para discussão, no entanto, sugiro que você discuta isso com outras pessoas no bate-papo ou faça uma nova pergunta no site principal.__init__
funções. especialmente se a assinatura de__init__
variar entre as classes na hierarquia. Eu adicionei uma resposta que incide sobre este aspectoEu brinquei um pouco
super()
e reconheci que podemos mudar a ordem de chamada.Por exemplo, temos a próxima estrutura hierárquica:
Nesse caso, o MRO de D será (apenas para Python 3):
Vamos criar uma classe onde
super()
chamadas após a execução do método.Portanto, podemos ver que a ordem de resolução é a mesma da MRO. Mas quando chamamos
super()
no início do método:Temos uma ordem diferente, é revertida uma ordem da tupla MRO.
Para leitura adicional, eu recomendaria as próximas respostas:
fonte
Tudo isso não pressupõe que a classe base seja uma classe de novo estilo?
Não funcionará no Python 2.
class A
deve ser de novo estilo, ou seja:class A(object)
fonte
Ao chamar
super()
para resolver a versão do pai de um método de classe, método de instância ou método estático, queremos passar a classe atual cujo escopo estamos no primeiro argumento, para indicar qual escopo do pai estamos tentando resolver e como um segundo argumento, o objeto de interesse para indicar a qual objeto estamos tentando aplicar esse escopo.Considere-se uma hierarquia de classe
A
,B
eC
onde cada classe é o progenitor do um que se lhe segue, ea
,b
, ec
respectivas ocorrências de cada.Usando
super
com um método staticpor exemplo, usando
super()
de dentro do__new__()
métodoExplicação:
1 - embora seja usual
__new__()
tomar como primeiro parâmetro uma referência à classe de chamada, ela não é implementada no Python como um método de classe, mas como um método estático. Ou seja, uma referência a uma classe deve ser passada explicitamente como o primeiro argumento ao chamar__new__()
diretamente:2- Ao chamar
super()
para chegar à classe pai, passamos a classe filhoA
como seu primeiro argumento, depois passamos uma referência ao objeto de interesse; nesse caso, é a referência da classe que foi passada quandoA.__new__(cls)
foi chamada. Na maioria dos casos, também é uma referência à classe filho. Em algumas situações, pode não ser, por exemplo, no caso de heranças de várias gerações.3- Como, como regra geral,
__new__()
é um método estático,super(A, cls).__new__
também retornará um método estático e precisará fornecer todos os argumentos explicitamente, incluindo a referência ao objeto de interesse, neste casocls
.4- fazer a mesma coisa sem
super
Usando
super
com um método de instânciapor exemplo, usando
super()
de dentro__init__()
Explicação:
1-
__init__
é um método de instância, o que significa que ele usa como primeiro argumento uma referência a uma instância. Quando chamada diretamente da instância, a referência é passada implicitamente, ou seja, você não precisa especificá-la:2- ao chamar
super()
dentro__init__()
, passamos a classe filho como o primeiro argumento e o objeto de interesse como um segundo argumento, que em geral é uma referência a uma instância da classe filho.3- A chamada
super(A, self)
retorna um proxy que resolverá o escopo e aplicaráself
como se fosse agora uma instância da classe pai. Vamos chamar esse proxys
. Como__init__()
é um método de instância, a chamadas.__init__(...)
passa implicitamente uma referênciaself
como o primeiro argumento para o pai__init__()
.4- fazer o mesmo sem
super
precisar passar uma referência a uma instância explicitamente para a versão do pai__init__()
.Usando
super
com um método de classeExplicação:
1 - Um método de classe pode ser chamado diretamente da classe e toma como primeiro parâmetro uma referência à classe.
2- Ao chamar
super()
dentro de um método de classe para resolver a versão do pai, queremos passar a classe filho atual como o primeiro argumento para indicar qual escopo do pai estamos tentando resolver e o objeto de interesse como o segundo argumento para indicar a qual objeto queremos aplicar esse escopo, que em geral é uma referência à própria classe filho ou a uma de suas subclasses.3- A chamada
super(B, cls)
resolve o escopoA
e aplica-acls
. Desde aalternate_constructor()
é um método de classe, a chamadasuper(B, cls).alternate_constructor(...)
passará implicitamente uma referênciacls
como o primeiro argumento paraA
a versão dealternate_constructor()
4- fazer o mesmo sem usar
super()
você precisará obter uma referência à versão não acoplada deA.alternate_constructor()
(ou seja, a versão explícita da função). Simplesmente fazer isso não funcionaria:O exemplo acima não funcionaria porque o
A.alternate_constructor()
método usa uma referência implícitaA
como seu primeiro argumento. O quecls
foi aprovado aqui seria seu segundo argumento.fonte
Muitas ótimas respostas, mas para os aprendizes visuais: primeiro vamos explorar com argumentos para super e depois sem.
Imagine que há uma instância
jack
criada a partir da classeJack
, que possui a cadeia de herança como mostrado em verde na figura. A ligar:super(Jack, jack).method(...)
usará o MRO (Ordem de resolução de método) de
jack
(sua árvore de herança em uma determinada ordem) e começará a pesquisarJack
. Por que alguém pode fornecer uma classe pai? Bem, se começarmos a pesquisar a partir da instânciajack
, ele encontrará o método da instância, o ponto principal é encontrar o método dos pais.Se alguém não fornece argumentos para super, é como o primeiro argumento passado é a classe de
self
, e o segundo argumento passado éself
. Estes são calculados automaticamente para você no Python3.No entanto, digamos que não queremos usar
Jack
o método, em vez de repassarJack
, poderíamos repassarJen
para começar a procurar o método a partir deJen
.Ele pesquisa uma camada de cada vez (largura, não profundidade), por exemplo, se
Adam
eSue
ambas tiverem o método necessário, a primeiraSue
será encontrada primeiro.Se
Cain
eSue
ambos tivessem o método necessário,Cain
o método de seria chamado primeiro. Isso corresponde no código a:O MRO é da esquerda para a direita.
fonte
algumas ótimas respostas aqui, mas elas não abordam como usar
super()
no caso em que diferentes classes na hierarquia têm assinaturas diferentes ... especialmente no caso de__init__
Para responder a essa parte e poder usar efetivamente,
super()
sugiro ler minha resposta super () e alterar a assinatura dos métodos cooperativos .aqui está apenas a solução para este cenário:
exemplo de uso:
resultado:
fonte
Isso é bastante fácil de entender.
Ok, o que acontece agora se você usar
super(Child,self)
?Quando uma instância filho é criada, sua MRO (ordem de resolução de método) fica na ordem de (filho, SomeBaseClass, objeto) com base na herança. (suponha que SomeBaseClass não tenha outros pais, exceto o objeto padrão)
Ao passar
Child, self
,super
pesquisa no MRO daself
instância e retorna o objeto proxy ao lado de Child, nesse caso é SomeBaseClass, esse objeto chama o__init__
método de SomeBaseClass. Em outras palavras, se forsuper(SomeBaseClass,self)
, o objeto proxy quesuper
retornar seriaobject
Para herança múltipla, o MRO pode conter muitas classes, portanto, basicamente,
super
você decide onde deseja iniciar a pesquisa no MRO.fonte
Considere o seguinte código:
Se alterar o construtor de
Y
paraX.__init__
, você obterá:Mas
super(Y, self).__init__()
, usando , você obterá:E
P
ouQ
pode até estar envolvido em outro arquivo que você não conhece quando escreveX
eY
. Então, basicamente, você não saberá o quesuper(Child, self)
fará referência quando estiver escrevendoclass Y(X)
, mesmo a assinatura de Y é tão simples quantoY(X)
. É por isso que super poderia ser uma escolha melhor.fonte