Pregunta

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)

En el código anterior, la comentada __init__ llama a comparecer al ser el comúnmente aceptado "inteligente" camino a la super clase de inicialización.Sin embargo, en el caso de que la jerarquía de clases es probable que cambie, he estado usando el descomentado forma, hasta hace poco.

Parece que en la llamada a la super constructor de B arriba en la jerarquía, que B.__init__ se llama de nuevo, self.__class__ es en realidad C, no B como yo siempre había asumido.

Hay alguna manera en Python-2.x que puedo mantener una adecuada MRO (con respecto a la inicialización de todos los padres de familia de las clases en el orden correcto) cuando se llama super constructores mientras no nombrar a la clase actual (la B en super(B, self).__init__(1, b, c))?

¿Fue útil?

Solución

Respuesta corta:. No, no hay ninguna manera de forma implícita invocar la __init__ derecha con los argumentos adecuados de la clase padre justo en Python 2.x

Por cierto, el código como se muestra aquí es incorrecta:. Si utiliza super () __init__, a continuación, todas las clases en la jerarquía deben tener la misma firma en sus métodos __init__. De lo contrario su código puede dejar de funcionar si se introduce una nueva subclase que utiliza herencia múltiple.

http://fuhm.net/super-harmful/ para una descripción más larga de el tema (con fotos).

Otros consejos

El código no tiene nada que ver con el método de resolución de orden.Método de resolución de trata en el caso de la herencia múltiple, que no es el caso de tu ejemplo.Su código es simplemente mal porque se supone que self.__class__ en realidad es la misma clase de la una, donde se define el método y esto es incorrecto:

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

así que cuando usted debe llamar a:

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

usted está, de hecho, llamar a:

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

EDITAR:tratando de responder mejor a la pregunta.

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

impresiones:

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

(Una versión modificada de plantilla de patrón).

Ahora los padres son los métodos que realmente llama implícitamente, pero tengo que estar de acuerdo con python zen donde explícito es mejor que implícita porque el código es menos legible y la ganancia es de pobres.Pero ten cuidado de que todos los _init los métodos tienen los mismos parámetros, no puede olvidarse completamente de los padres y no pretendo hacerlo.

Para la herencia simple, un mejor enfoque es una llamada explícita a los padres del método, sin invocar super.Haciendo así que usted no tiene que nombre de la clase actual, pero todavía debe de atención acerca de quién es el padre de la clase.

Buenas lecturas son: cómo pitones-super-hacer-el-derecho-cosa y los enlaces que se sugiere en la pregunta y en la particularidad Python es Super ingeniosa, pero no puede usarlo

Si la jerarquía es probable que cambie los síntomas de un mal diseño y tiene consecuencias en todas las partes que están usando ese código y no debe ser alentado.

EDIT 2

Otro ejemplo que se me viene en mente, pero que utiliza metaclasses.Urwid biblioteca utiliza metaclass para almacenar un atributo, __super, en clase, de modo que sólo se necesita para acceder a ese 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
>>> 

Tal vez lo que busca es metaclases?

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()

Las impresiones "Soy la clase A"

¿Qué significa la metaclase está anulando la creación inicial de la clase B (no el objeto) y se asegura de que el diccionario incorporado para cada objeto B contiene ahora una matriz bases donde se pueden encontrar todos los baseclasses para B

Que yo sepa, no se hace comúnmente lo siguiente. Pero parece que no funciona.

Los métodos de una definición de clase dada siempre atribuye mangle doble subrayado para incluir el nombre de la clase que están definidas. Por lo tanto, si STASH una referencia a la clase en forma de nombre destrozado, donde las instancias pueden verlo, puede utilizar esa en la llamada a super.

Un ejemplo esconder las referencias sobre el objeto en sí mismo, mediante la aplicación de __new__ en la clase 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

Y, haciendo una cosa similar, pero usando un meta-clase y esconder las referencias __class de la clase de objetos mismos:

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

La ejecución de esta todos juntos, como un archivo fuente:

% 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top