Domanda

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)
chiamate __init__

Nel codice precedente, il commentato sembrano l'essere il modo comunemente accettato "intelligente" di fare l'inizio super-classe. Tuttavia nel caso in cui la gerarchia di classe è destinata a cambiare, ho utilizzato la forma senza commenti, fino a poco tempo.

Sembra che nella chiamata al super costruttore per B nella gerarchia di cui sopra, che B.__init__ si chiama ancora una volta, è in realtà self.__class__ C, non B come avevo sempre pensato.

C'è qualche modo in Python-2.x che posso mantenere la corretta MRO (rispetto a inizializzare tutte le classi genitore nell'ordine corretto) quando si chiama costruttori di super pur non nominando la classe corrente (il B in a super(B, self).__init__(1, b, c))?

È stato utile?

Soluzione

Risposta breve:. No, non c'è alcun modo per richiamare implicitamente la __init__ giusto con le giuste argomentazioni della classe genitore destra in Python 2.x

Per inciso, il codice come mostrato qui non è corretto:. Se si utilizza super () __init__, quindi tutte le classi nella gerarchia deve avere la stessa firma nei loro metodi __init__. In caso contrario, il codice può smettere di funzionare se si introduce una nuova sottoclasse che utilizza l'ereditarietà multipla.

http://fuhm.net/super-harmful/ per una descrizione più il problema (con immagini).

Altri suggerimenti

Il tuo codice non ha nulla a che fare con ordine di risoluzione metodo. Risoluzione Metodo è disponibile in caso di ereditarietà multipla, che non è il caso del vostro esempio. Il tuo codice è semplicemente sbagliato, perché si assume che self.__class__ è in realtà la stessa classe di quella in cui è definito il metodo e questo è sbagliato:

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

in modo che quando si dovrebbe chiamare:

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

si sono effettivamente chiamando:

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

Modifica :. Cercando di rispondere meglio alla domanda

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

stampe:

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

(Una versione modificata di mascherina modello ).

Ora metodi genitori sono in realtà chiamato implicitamente, ma io sono d'accordo con python zen dove esplicito è meglio che implicita perché il codice è meno leggibile e il guadagno è scarsa. Ma attenzione che tutti i metodi _init hanno gli stessi parametri, non si può completamente dimenticare i genitori e io non suggerisco di farlo.

Per l'ereditarietà singola, un approccio migliore sta chiamando esplicitamente il metodo del genitore, senza invocare super. In questo modo non c'è bisogno di nome della classe corrente , ma ancora si deve preoccuparsi di chi è di classe dei genitori.

Buona legge sono: Come se-pitoni-super-do -la-destra-cosa ei collegamenti ha suggerito in questa domanda e nella particolarità Super di Python è ingegnoso, ma non è possibile utilizzarlo

Se la gerarchia è destinata a cambiare è sintomi di cattiva progettazione e ha conseguenze in tutte le parti che utilizzano il codice e non deve essere incoraggiata.

Modifica 2

Un altro esempio mi viene in mente, ma che utilizza metaclassi. Urwid utilizza metaclasse per memorizzare un attributo, __super, in classe in modo che è necessario solo per accedere a tale attributo.

Esempio:

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

Forse quello che stai cercando è metaclassi?

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

Stampe "Sono in classe A"

Quello che il metaclasse fa è ignorando la creazione iniziale della classe B (non l'oggetto) e fa in modo che il dizionario incorporato per ciascun oggetto B contiene ora una matrice basi dove si possono trovare tutte le baseclasses per B

Per quanto ne so, quanto segue non è comunemente fatto. Ma non sembra funzionare.

Metodi in una determinata definizione di classe schiante sempre a doppia sottolineatura attributi per includere il nome della classe che stanno definiti. Quindi, se si stash un riferimento alla classe in nome storpiato-forma in cui le istanze possono vederlo, è possibile utilizzare tale nella chiamata a super.

Un esempio stashing i riferimenti dell'oggetto stesso, implementando __new__ sul baseclass:

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, facendo una cosa simile, ma utilizzando un meta-class e stashing i riferimenti __class sulla classe stessi oggetti:

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

L'esecuzione di questo tutti insieme, come un file sorgente:

% 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>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top