Implicitamente invocando genitore classe di inizializzazione
-
23-09-2019 - |
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)
)?
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>