Stai speculando molto, mentre il minimalista di Python e "Casi speciali non sono abbastanza speciali da infrangere le regole". direttiva, renderne più facile la comprensione.
In Python2, un attributo __metaclass__
nel corpo della classe viene utilizzato in fase di creazione della classe per chiamare la "classe" che sarà la classe. Di solito è la classe denominata type
.Per chiarire, quel momento è dopo che il parser ha analizzato il corpo della classe, dopo che il compilatore lo ha compilato su un oggetto codice e dopo che è stato effettivamente eseguito al momento dell'esecuzione del programma, e solo se __metaclass__
è esplicitamente fornito nel corpo della classe.
Così modo di controllo che li ha lasciati va in un caso come:
class A(object):
__metaclass__ = MetaA
class B(A):
pass
A
ha __metaclass__
nel suo corpo - MetaA
è chiamato invece di type
di farne "oggetto di classe". B
non ha __metaclass__
nel suo corpo. Dopo che è stato creato, se si tenta semplicemente di accedere all'attributo __metaclass__
, è un attributo come anyother, che sarà visibile perché Python lo utilizzerà dalla superclasse A
. Se si controlla A.__dict__
vedrete il __metaclass__
e se si controlla B.__dict__
no.
Questo attributo A.__metaclass__
è non utilizzato quando viene creato B. Se lo modifichi in A
prima di dichiarare B
utilizzerai ancora lo stesso metaclasse di A
- perché Python usa il tipo della classe genitore come metaclasse nell'assenza della dichiarazione di un esplicito __metaclass__
.
Facciamo un esempio:
In [1]: class M(type): pass
In [2]: class A(object): __metaclass__ = M
In [3]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(A.__class__, A.__metaclass__, A.__dict__.get("__metaclass__"), type(A))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: <class '__main__.M'>, type: <class '__main__.M'>
In [4]: class B(A): pass
In [5]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(B.__class__, B.__metaclass__, B.__dict__.get("__metaclass__"), type(B))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: None, type: <class '__main__.M'>
In [6]: A.__metaclass__ = type
In [8]: class C(A): pass
In [9]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(C.__class__, C.__metaclass__, C.__dict__.get("__metaclass__"), type(C))
class: <class '__main__.M'>, metaclass_attr: <type 'type'>, metaclass_in_dict: None, type: <class '__main__.M'>
Inoltre, se si tenta di creare solo una classe attraverso una chiamata a type
invece di utilizzare un corpo con una dichiarazione class
, __metaclass__
è anche solo un attributo comune:
In [11]: D = type("D", (object,), {"__metaclass__": M})
In [12]: type(D)
type
Riassumendo fino ad ora: l'attributo __metaclass__
in Python 2 è speciale solo se è inserito esplicitamente nel corpo della classe dichiarazione, come parte dell'esecuzione della dichiarazione di blocco class
. È un attributo ordinario senza proprietà speciali in seguito.
Python3 si sono liberati di questo strano "attributo __metaclass__
non valido ora" e hanno consentito un'ulteriore personalizzazione del corpo della classe modificando la sintassi per specificare i metaclassi. (E 'come dichiarato come se si trattasse di un "metaclass
nome parametro" sul conto class
stesso)
Ora, per la seconda parte di quello sollevato i dubbi: se nel metodo del meta-classe __new__
si chiama type
invece di type.__new__
, non c'è modo in cui Python possa "sapere" type
viene chiamato da una metaclass derivata. Quando chiami type.__new__
, passi come primo parametro l'attributo cls
del tuo metaclasse __new__
stesso è stato passato dal runtime: questo è ciò che contrassegna la classe risultante come un'istanza di una sottoclasse di type
.Questo è proprio come l'ereditarietà funziona per qualsiasi altra classe in Python - così "no comportamenti particolari" qui:
Quindi, individuare la differenza:
class M1(type):
def __new__(metacls, name, bases, attrs):
cls = type.__new__(metacls, name, bases, attrs)
# cls now is an instance of "M1"
...
return cls
class M2(type):
def __new__(metacls, name, bases, attrs):
cls = type(cls, name, bases, attrs)
# Type does not "know" it was called from within "M2"
# cls is an ordinary instance of "type"
...
return cls
Lo si può vedere nel prompt interattivo:
In [13]: class M2(type):
....: def __new__(metacls, name, bases, attrs):
....: return type(name, bases, attrs)
....:
In [14]: class A(M2): pass
In [15]: type(A)
Out[15]: type
In [16]: class A(M2): __metaclass__ = M2
In [17]: A.__class__, A.__metaclass__
Out[17]: (type, __main__.M2)
(si noti che il metodo di primo parametro metaclasse __new__
è il metaclasse per sé, quindi, più propriamente chiamato metacls
rispetto cls
come nel codice, e io na sacco di codice "in the wild")
Penso che abbia a che fare con type .__ new__ e tipo http://stackoverflow.com/questions/2608708/what-is-the-difference-between-type-and -tipo-new-in-python –