TypeErrors utilizzando metaclassi in collaborazione con l'ereditarietà multipla
-
18-09-2019 - |
Domanda
Ho due domande converning metaclassi e l'ereditarietà multipla. La prima è:? Perché ricevo un TypeError per la Derived
classe, ma non per Derived2
class Metaclass(type): pass
class Klass(object):
__metaclass__ = Metaclass
#class Derived(object, Klass): pass # if I uncomment this, I get a TypeError
class OtherClass(object): pass
class Derived2(OtherClass, Klass): pass # I do not get a TypeError for this
Il messaggio di errore esatto è:
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases object, Klass
La seconda domanda è: Perché super
non funziona in questo caso (se uso __init__
invece di __new__
, super
funziona di nuovo):
class Metaclass(type):
def __new__(self, name, bases, dict_):
return super(Metaclass, self).__new__(name, bases, dict_)
class Klass(object):
__metaclass__ = Metaclass
Non ottengo:
TypeError: Error when calling the metaclass bases type.__new__(X):
X is not a type object (str)
Sto usando Python 2.6.
Soluzione
La seconda questione è già stato ben risposto due volte, anche se __new__
è in realtà uno staticmethod, non un classmethod come erroneamente affermato in un commento ...:
>>> class sic(object):
... def __new__(cls, *x): return object.__new__(cls, *x)
...
>>> type(sic.__dict__['__new__'])
<type 'staticmethod'>
La prima domanda (come qualcuno ha notato) non ha nulla a che fare con metaclassi: semplicemente non è possibile moltiplicare ereditare da qualsiasi due classi A e B in questo ordine dove B è una sottoclasse di A. per esempio:.
>>> class cis(sic): pass
...
>>> class oops(sic, cis): pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution
order (MRO) for bases sic, cis
Il MRO garantisce che le basi più a sinistra sono visitati prima di quelli più a destra - ma garantisce anche che tra gli antenati se x è una sottoclasse di y allora x è visitato prima y. E 'impossibile soddisfare entrambe queste garanzie in questo caso. C'è una buona ragione per queste garanzie, naturalmente: senza di loro (ad esempio, nelle classi di vecchio stile, che garantiscono solo l'ordine da sinistra a destra nella risoluzione metodo, non il vincolo sottoclasse) tutte le modifiche in x sarebbe stato ignorato a favore delle definizioni di y, e che non può avere molto senso. Pensateci: che cosa fa significa ereditare da object
prima, e da qualche altra seconda classe? (Definizione ;-) essenzialmente inesistente Quel object
dei suoi diversi metodi speciali deve avere la precedenza su l'altra classe di, provocando le sostituzioni di altra classe per essere ignorato?
Altri suggerimenti
Per la prima domanda, uno sguardo alla la descrizione di MRO in python - in particolare, la sezione "cattivo Metodo ordine Risoluzione". In sostanza, è a che fare con il fatto che Python non sa se utilizzare metodi di un oggetto o di Klass. (Non è niente a che fare con l'utilizzo di metaclassi.)
Per la seconda domanda, sembra che stai incomprensione come funziona la funzione __new__
. Non ci vuole un riferimento a se stesso come il primo argomento - ci vuole un riferimento al tipo di classe che viene istanziata. Quindi, il codice dovrebbe essere simile a questo:
class Metaclass(type):
def __new__(cls, name, bases, dictn):
return type.__new__(cls, name, bases, dictn)
Per la seconda domanda, è necessario passare a sé __new__
in questo modo:
class Metaclass(type):
def __new__(self, name, bases, dict_):
return super(Metaclass, self).__new__(self, name, bases, dict_)
class Klass(object):
__metaclass__ = Metaclass
Non riesco a ricordare la parte superiore della mia testa perché questo è, ma credo che sia perché type.__new__
non è un metodo vincolato e quindi non magicamente ottenere l'argomento sé.
Perché si dovrebbe fare?
class Derived(object, Klass):
Klass deriva già da oggetto.
class Derived(Klass):
è la cosa ragionevole qui.