Pergunta

class A(object):
    def __init__(self, a, b, c):
        #super(A, self).__init__()
        super(self.__class__, self).__init__()


class B(A):
    def __init__(self, b, c):
        print super(B, self)
        print super(self.__class__, self)
        #super(B, self).__init__(1, b, c)
        super(self.__class__, self).__init__(1, b, c)

class C(B):
    def __init__(self, c):
        #super(C, self).__init__(2, c)
        super(self.__class__, self).__init__(2, c)
C(3)

No código acima, o comentado __init__ As chamadas parecem ser a maneira "inteligente" comumente aceita para fazer a inicialização da super classe. No entanto, no caso de a hierarquia de classe mudar, eu tenho usado a forma não dominada, até recentemente.

Parece que na chamada para o super construtor para B Na hierarquia acima, isso B.__init__ é chamado de novo, self.__class__ é na verdade C, não B Como eu sempre assumi.

Existe alguma maneira no Python-2.x que eu possa manter o MRO adequado (com relação à inicialização de todas as classes dos pais na ordem correta) ao chamar super construtores, sem nomear a classe atual (o B in super(B, self).__init__(1, b, c))?

Foi útil?

Solução

Resposta curta: Não, não há como invocar implicitamente o direito __init__ Com os argumentos corretos da classe pai certa no Python 2.x.

Aliás, o código como mostrado aqui está incorreto: se você usar super ().__init__, então todas as classes em sua hierarquia devem ter a mesma assinatura em seus __init__ métodos. Caso contrário, seu código poderá parar de funcionar se você introduzir uma nova subclasse que usa herança múltipla.

Ver http://fuhm.net/super-harmful/ Para uma descrição mais longa do problema (com fotos).

Outras dicas

Seu código não tem nada a ver com a ordem de resolução do método. A resolução do método vem no caso de herança múltipla, o que não é o caso do seu exemplo. Seu código está simplesmente errado porque você assume que self.__class__ é realmente a mesma classe daquele em que o método é definido e isso está errado:

>>> class A(object):
...     def __init__(self):
...         print self.__class__
... 
>>> 
>>> class B(A):
...     def __init__(self):
...         A.__init__(self)
... 
>>> B()
<class '__main__.B'>
<__main__.B object at 0x1bcfed0>
>>> A()
<class '__main__.A'>
<__main__.A object at 0x1bcff90>
>>> 

Então, quando você deve ligar:

super(B, self).__init__(1, b, c)

Você está realmente ligando:

# super(self.__class__, self).__init__(1, b, c)
super(C, self).__init__(1, b, c)

EDITAR: tentando responder melhor à pergunta.

class A(object):
    def __init__(self, a):
        for cls in self.__class__.mro():
            if cls is not object:
                cls._init(self, a)
    def _init(self, a):
        print 'A._init'
        self.a = a

class B(A):
    def _init(self, a):
        print 'B._init'

class C(A):
    def _init(self, a):
        print 'C._init'

class D(B, C):
    def _init(self, a):
        print 'D._init'


d = D(3)
print d.a

impressões:

D._init
B._init
C._init
A._init
3

(Uma versão modificada de padrão de modelo).

Agora, os métodos dos pais são realmente chamados implicitamente, mas tenho que concordar com o python zen onde explícito é melhor do que implícito porque o código é menor legível e o ganho é ruim. Mas tenha cuidado com tudo _init Os métodos têm os mesmos parâmetros, você não pode esquecer completamente os pais e eu não sugiro fazê -lo.

Para herança única, uma abordagem melhor está chamando explicitamente o método dos pais, sem invocar super. Fazendo isso você não precisa Nomeie a classe atual, mas você ainda deve se preocupar com quem é a classe dos pais.

Boas leituras são: Como fazer-pythons-super-the-right-rathing e os links sugeridos nessa questão e em particular O Super do Python é bacana, mas você não pode usá -lo

Se é provável que a hierarquia mude seja sintomas de design ruim e tenha consequências em todas as partes que estão usando esse código e não devem ser incentivadas.

Editar 2

Outro exemplo me vem em mente, mas que usa metaclasses. Biblioteca Urwid usa metaclasse Para armazenar um atributo, __super, na aula para que você precise acessar esse atributo.

Ex:

>>> class MetaSuper(type):
...     """adding .__super"""
...     def __init__(cls, name, bases, d):
...         super(MetaSuper, cls).__init__(name, bases, d)
...         if hasattr(cls, "_%s__super" % name):
...             raise AttributeError, "Class has same name as one of its super classes"
...         setattr(cls, "_%s__super" % name, super(cls))
... 
>>> class A:
...  __metaclass__ = MetaSuper
...  def __init__(self, a):
...   self.a = a
...   print 'A.__init__'
... 
>>> class B(A):
...  def __init__(self, a):
...   print 'B.__init__'
...   self.__super.__init__(a)
... 
>>> b = B(42)
B.__init__
A.__init__
>>> b.a
42
>>> 

Talvez o que você está procurando seja metaclasses?

class metawrap(type):
    def __new__(mcs,name, bases, dict):
        dict['bases'] = bases
        return type.__new__(mcs,name,bases,dict)

class A(object):
    def __init__(self):
        pass
    def test(self):
        print "I am class A"

class B(A):
    __metaclass__ = metawrap
    def __init__(self):
        pass
    def test(self):
        par = super(self.bases[0],self)
        par.__thisclass__.test(self)
foo = B()
foo.test()

Impressões "Eu sou classe A"

O que o metaclass faz é substituir a criação inicial da classe B (não o objeto) e garante que o dicionário incorporado para cada objeto B agora contenha uma matriz de bases, onde você pode encontrar todas as linhas de base para B

Que eu saiba, o seguinte não é comumente feito. Mas parece funcionar.

Métodos Em uma determinada definição de classe, sempre manifestam atributos duplos de Underscore para incluir o nome da classe em que eles são definidos. Portanto, se você esconder uma referência à classe em forma de nomes em que as instâncias podem vê-la, você pode usar isso na chamada para super.

Um exemplo escondendo as referências sobre o próprio objeto, implementando __new__ na base de base:

def mangle(cls, name):
    if not name.startswith('__'):
        raise ValueError('name must start with double underscore')
    return '_%s%s' % (cls.__name__, name)

class ClassStasher(object):
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        for c in cls.mro():
            setattr(obj, mangle(c, '__class'), c)
        return obj

class A(ClassStasher):
    def __init__(self):
        print 'init in A', self.__class
        super(self.__class, self).__init__()

class B(A):
    def __init__(self):
        print 'init in B', self.__class
        super(self.__class, self).__init__()

class C(A):
    def __init__(self):
        print 'init in C', self.__class
        super(self.__class, self).__init__()

class D(B, C):
    def __init__(self):
        print 'init in D', self.__class
        super(self.__class, self).__init__()


d = D()    
print d

E, fazendo uma coisa semelhante, mas usando uma meta-classe e guardando o __class Referências sobre os próprios objetos de classe:

class ClassStasherType(type):
    def __init__(cls, name, bases, attributes):
        setattr(cls, mangle(cls, '__class'), cls)

class ClassStasher(object):
    __metaclass__ = ClassStasherType

class A_meta(ClassStasher):
    def __init__(self):
        print 'init in A_meta', self.__class
        super(self.__class, self).__init__()

class B_meta(A_meta):
    def __init__(self):
        print 'init in B_meta', self.__class
        super(self.__class, self).__init__()

class C_meta(A_meta):
    def __init__(self):
        print 'init in C_meta', self.__class
        super(self.__class, self).__init__()

class D_meta(B_meta, C_meta):
    def __init__(self):
        print 'init in D_meta', self.__class
        super(self.__class, self).__init__()


d = D_meta()    
print d

Executando tudo isso juntos, como um arquivo de origem:

% python /tmp/junk.py
init in D <class '__main__.D'>
init in B <class '__main__.B'>
init in C <class '__main__.C'>
init in A <class '__main__.A'>
<__main__.D object at 0x1004a4a50>
init in D_meta <class '__main__.D_meta'>
init in B_meta <class '__main__.B_meta'>
init in C_meta <class '__main__.C_meta'>
init in A_meta <class '__main__.A_meta'>
<__main__.D_meta object at 0x1004a4bd0>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top