2010-04-09 22 views
11

stavo scrivendo un metaclasse e accidentalmente fatto in questo modo:Qual è la differenza tra type e type .__ new__ in python?

class MetaCls(type): 
    def __new__(cls, name, bases, dict): 
     return type(name, bases, dict) 

... invece di come questo:

class MetaCls(type): 
    def __new__(cls, name, bases, dict): 
     return type.__new__(cls, name, bases, dict) 

Che cosa è esattamente la differenza tra questi due metaclassi? E più precisamente, cosa ha causato il primo a non funzionare correttamente (alcune classi non sono state chiamate dal metaclass)?

risposta

6

Nel primo esempio si sta creando una nuova classe:

>>> class MetaA(type): 
...  def __new__(cls, name, bases, dct): 
...   print 'MetaA.__new__' 
...   return type(name, bases, dct) 
...  def __init__(cls, name, bases, dct): 
...   print 'MetaA.__init__' 
... 
>>> class A(object): 
...  __metaclass__ = MetaA 
... 
MetaA.__new__ 
>>> 

mentre nel secondo caso si sta chiamando i genitori __new__:

>>> class MetaA(type): 
...  def __new__(cls, name, bases, dct): 
...   print 'MetaA.__new__' 
...   return type.__new__(cls, name, bases, dct) 
...  def __init__(cls, name, bases, dct): 
...   print 'MetaA.__init__' 
... 
>>> class A(object): 
...  __metaclass__ = MetaA 
... 
MetaA.__new__ 
MetaA.__init__ 
>>> 
1

È tutto descritto piuttosto bene here.

Se non si restituisce il tipo giusto di oggetto, non ha senso definire un metaclass personalizzato.

+1

Ma questa è la domanda che voglio dire: perché non 'type' restituire il giusto tipo di oggetto? So che devo usare il metodo '__new__' della sottoclasse, ma com'è diverso dall'uso di' type'? –

+1

In che modo si suppone che il tipo sappia magicamente quale tipo di oggetto si desidera creare? –

+0

bene, perché lo dico. Ho passato in nome, basi e dettato. Quale altra informazione ho bisogno di dargli? –

3
return type(name, bases, dict) 

Quello che si ottiene indietro da questo è un nuovo type, e non un'istanza MetaCls a tutti. Di conseguenza, non è possibile chiamare i metodi definiti in MetaCls (incluso __init__).

type.__new__ sarà chiamato come parte della creazione di quel nuovo tipo, sì, ma il valore di cls andare in quella funzione sta per essere type e non MetaCls.

3
class MyMeta(type): 
    def __new__(meta, cls, bases, attributes): 
     print 'MyMeta.__new__' 
     return type.__new__(meta, cls, bases, attributes) 
    def __init__(clsobj, cls, bases, attributes): 
     print 'MyMeta.__init__' 

class MyClass(object): 
    __metaclass__ = MyMeta 
    foo = 'bar' 

Un altro modo per raggiungere lo stesso risultato:

cls = "MyClass" 
bases =() 
attributes = {'foo': 'bar'} 
MyClass = MyMeta(cls, bases, attributes) 

MyMeta è un callable quindi Python utilizzerà il metodo speciale __call__.

Python cercherà __call__ in s' tipo il MyMeta (che è type nel nostro caso)

"Per classi di nuovo stile, invocazioni implicite di metodi speciali sono garantiti solo per funzionare correttamente se definita il tipo di un oggetto, non nel dizionario dell'istanza dell'oggetto"

MyClass = MyMeta(...) viene interpretato come:

my_meta_type = type(MyMeta) 
MyClass = my_meta_type.__call__(MyMeta, cls, bases, attributes) 

All'interno del type.__call__() immagino qualcosa di simile:

MyClass = MyMeta.__new__(MyMeta, cls, bases, attributes) 
meta_class = MyClass.__metaclass__ 
meta_class.__init__(MyClass, cls, bases, attributes) 
return MyClass 

MyMeta.__new__() deciderà come il MyClass è costruito:

type.__new__(meta, cls, bases, attributes) imposterà la metaclasse corretto (che è MyMeta) per MyClass

type(cls, bases, attributes) imposterà il metaclasse predefinito (che è il tipo) per MyClass

4

Fare riferimento all'annotazione riportata di seguito, sperare questo utile.

class MetaCls(type): 
    def __new__(cls, name, bases, dict): 
     # return a new type named "name",this type has nothing 
     # to do with MetaCls,and MetaCl.__init__ won't be invoked 
     return type(name, bases, dict) 

class MetaCls(type): 
    def __new__(cls, name, bases, dict): 
     # return a new type named "name",the returned type 
     # is an instance of cls,and cls here is "MetaCls", so 
     # the next step can invoke MetaCls.__init__ 
     return type.__new__(cls, name, bases, dict) 
8

La prima cosa che dovete capire è come object.__new__() opere.

Qui è dal documentation:

object.\__new__(cls[, ...])

Chiamato per creare un nuova istanza della classe cls. __new__() è un metodo statico (speciale, quindi non è necessario dichiararlo come tale) che accetta la classe di cui è stata richiesta un'istanza come primo argomento. Gli argomenti rimanenti sono quelli passati all'espressione costruttore oggetto (la chiamata alla classe). Il valore restituito di __new__() dovrebbe essere la nuova istanza dell'oggetto (in genere un'istanza di cls).

implementazioni tipiche creare una nuova istanza della classe richiamando metodo della superclasse __new__() usando super(currentclass, cls).__new__(cls[, ...]) con gli argomenti appropriati e modificando l'istanza appena creato come necessario prima di restituirla.

Se __new__() restituisce un'istanza di cls, allora il metodo __init__() della nuova istanza verrà invocato come __init__(self[, ...]), dove self è la nuova istanza e gli altri argomenti sono gli stessi di sono stati passati al __new__().

Se __new__() non restituisce un istanza di cls, allora il metodo __init__() della nuova istanza non verrà richiamato.

__new__() è destinato principalmente a consentire sottoclassi di immutabili tipi (come int, str o tuple) per personalizzare creazione dell'istanza. È anche comunemente sovrascritto in metaclassi personalizzati per personalizzare la creazione della classe .

Quindi, in mg di answer., Il primo non chiamata di funzione __init__ mentre la seconda chiamate di funzione __init__ dopo aver chiamato __new__.

+0

In che modo 'type .__ new__' è correlato a' object .__ new__'? Non ho avuto quella parte. – Nishant

Problemi correlati