Por que a super () mágica do Python 3.x?


159

No Python 3.x, super()pode ser chamado sem argumentos:

class A(object):
    def x(self):
         print("Hey now")

class B(A):
    def x(self):
        super().x()
>>> B().x()
Hey now

A fim de fazer este trabalho, um pouco de magia em tempo de compilação é executada, uma conseqüência do que é que o código a seguir (que religa supera super_) falha:

super_ = super

class A(object):
    def x(self):
        print("No flipping")

class B(A):
    def x(self):
        super_().x()
>>> B().x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in x
RuntimeError: super(): __class__ cell not found

Por que super()não é possível resolver a superclasse no tempo de execução sem a assistência do compilador? Existem situações práticas em que esse comportamento, ou a razão subjacente, poderia morder um programador incauto?

... e, como uma pergunta paralela: existem outros exemplos no Python de funções, métodos etc. que podem ser quebrados ao reconectá-los a um nome diferente?


6
Vou deixar Armin fazer o que explica sobre este um . Este também é outro bom post
Games Brainiac

Respostas:


216

O novo super()comportamento mágico foi adicionado para evitar violar o princípio DRY (Não se repita), veja PEP 3135 . Ter que nomear explicitamente a classe referenciando-a como global também é propenso aos mesmos problemas de religação que você descobriu consigo super():

class Foo(Bar):
    def baz(self):
        return super(Foo, self).baz() + 42

Spam = Foo
Foo = something_else()

Spam().baz()  # liable to blow up

O mesmo se aplica ao uso de decoradores de classe em que o decorador retorna um novo objeto, que religa o nome da classe:

@class_decorator_returning_new_class
class Foo(Bar):
    def baz(self):
        # Now `Foo` is a *different class*
        return super(Foo, self).baz() + 42

A super() __class__célula mágica evita esses problemas perfeitamente, fornecendo acesso ao objeto de classe original.

O PEP foi iniciado por Guido, que inicialmente imaginou superse tornar uma palavra-chave , e a idéia de usar uma célula para pesquisar a classe atual também era dele . Certamente, a ideia de transformá-la em palavra-chave fazia parte do primeiro rascunho do PEP .

No entanto, foi de fato o próprio Guido quem se afastou da ideia de palavra-chave como 'muito mágica' , propondo a implementação atual. Ele antecipou que usar um nome diferente para super()poderia ser um problema :

Meu patch usa uma solução intermediária: assume que você precisa __class__ sempre que você usa uma variável chamada 'super'. Assim, se você (globalmente) renomear supera suppere uso supper, mas não super, ele não vai funcionar sem argumentos (mas será ainda trabalho, se você passá-lo tanto __class__ou o objeto de classe real); se você tiver uma variável não relacionada denominada super, tudo funcionará, mas o método usará o caminho de chamada um pouco mais lento usado para variáveis ​​de célula.

Então, no final, foi o próprio Guido que proclamou que o uso de uma superpalavra - chave não parecia certo e que fornecer uma __class__célula mágica era um compromisso aceitável.

Concordo que o comportamento implícito e mágico da implementação é um tanto surpreendente, mas super()é uma das funções mais mal aplicadas na linguagem. Basta dar uma olhada em todas as invenções super(type(self), self)ou aplicações incorretas super(self.__class__, self) encontradas na Internet; se algum desses códigos fosse chamado de uma classe derivada, você acabaria com uma exceção de recursão infinita . No mínimo, a super()chamada simplificada , sem argumentos, evita esse problema.

Quanto ao renomeado super_; referência apenas __class__em seu método bem e ele vai trabalhar novamente. A célula é criada se você fizer referência aos nomes super ou __class__ no seu método:

>>> super_ = super
>>> class A(object):
...     def x(self):
...         print("No flipping")
... 
>>> class B(A):
...     def x(self):
...         __class__  # just referencing it is enough
...         super_().x()
... 
>>> B().x()
No flipping

1
Boa redação. Ainda é tão claro quanto a lama. Você está dizendo que super () é equivalente a uma função instanciada automaticamente como uma def super(of_class=magic __class__)espécie de self.super(); def super(self): return self.__class__?
22614 Charles

17
@CharlesMerriam: Este post não é sobre como super()funciona sem argumentos; trata principalmente do porquê existe. super(), em um método de classe, é equivalente a super(ReferenceToClassMethodIsBeingDefinedIn, self), onde ReferenceToClassMethodIsBeingDefinedIné determinado em tempo de compilação, anexado ao método como um fechamento nomeado __class__e super()procurará ambos no quadro de chamada em tempo de execução. Mas você realmente não precisa saber tudo isso.
Martijn Pieters

1
@CharlesMerriam: mas super()não chega nem perto de ser uma função instanciada automaticamente , não.
Martijn Pieters

1
@ chris.leonard: a frase-chave é A célula é criada se você usar super () ou usar __class__no seu método . Você usou o nome superem sua função. O compilador vê isso e adiciona o __class__fechamento.
Martijn Pieters

4
@Alexey: é não o suficiente. type(self)fornece o tipo atual , que não é igual ao tipo em que o método está definido. Portanto, uma classe Foocom método bazprecisa super(Foo, self).baz(), porque pode ser subclassificada como class Ham(Foo):, nesse momento type(self)é Hame super(type(self), self).baz()daria a você um loop infinito. Veja o post ao qual vinculo minha resposta: Ao chamar super () em uma classe derivada, posso passar em self .__ class__?
Martijn Pieters
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.