2010-04-09 4 views
11

je en train d'écrire un hasard et fait métaclasse comme ceci:Quelle est la différence entre le type et le type .__ new__ en python?

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

... au lieu de comme ceci:

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

Quelle est exactement la différence entre ces deux métaclasses? Et plus précisément, qu'est-ce qui a causé le dysfonctionnement du premier (certaines classes n'ont pas été appelées par la métaclasse)?

Répondre

6

Dans le premier exemple vous créez une nouvelle 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__ 
>>> 

alors que dans le second cas, vous appelez __new__ de parent:

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

Tout est décrit plutôt bien here.

Si vous ne renvoyez pas le bon type d'objet, il est inutile de définir une métaclasse personnalisée.

+1

Mais c'est la question que je me pose: pourquoi 'type' ne retourne-t-il pas le bon type d'objet? Je sais que je dois utiliser la méthode '__new__' de la sous-classe, mais en quoi est-ce différent de l'utilisation de' type'? –

+1

Comment le type est-il censé savoir par magie quel genre d'objet vous voulez qu'il fasse? –

+0

bien, parce que je le dis. J'ai passé en nom, bases et dict. De quelle autre information ai-je besoin? –

3
return type(name, bases, dict) 

Ce que vous obtenez en retour de c'est une nouvelle type, et non une instance MetaCls du tout. Par conséquent, vos méthodes définies dans MetaCls (y compris __init__) ne peuvent jamais être appelées.

type.__new__ seront appelés dans le cadre de la création de ce nouveau type, oui, mais la valeur de cls entrer dans cette fonction va être type et 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' 

Une autre façon d'atteindre le même résultat:

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

MyMeta est un callable donc Python utilisera la méthode spéciale __call__.

Python recherchera __call__ dans le type de MyMeta (qui est type dans notre cas)

« Pour les classes de type nouveau, les invocations implicites des méthodes spéciales sont seulement garanti pour fonctionner correctement si elle est définie sur un type d'objet, pas dans l'instance de l'objet dictionnaire »

MyClass = MyMeta(...) est interprété comme:

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

A l'intérieur du type.__call__() j'imagine quelque chose comme ceci:

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

MyMeta.__new__() décidera comment le MyClass est construit:

type.__new__(meta, cls, bases, attributes) va régler la métaclasse correcte (ce qui est MyMeta) pour MyClass

type(cls, bases, attributes) définira la métaclasse par défaut (qui est le type) pour MyClass

4

S'il vous plaît se référer à l'annotation ci-dessous, espérons que cela 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 première chose que vous devez savoir est comment object.__new__() fonctionne.

Ici, il est de la documentation:

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

Appelés à créer une nouvelle instance de la classe cls. __new__() est une méthode statique (spécial-cased donc vous n'avez pas besoin de le déclarer comme tel) qui prend la classe dont une instance a été demandée en tant que son premier argument. Les arguments restants sont ceux passés au constructeur d'objet expression (l'appel à la classe). La valeur de retour de __new__() doit être la nouvelle instance d'objet (généralement une instance de cls).

implémentations typiques créer une nouvelle instance de la classe en invoquant méthode __new__() de la superclasse en utilisant super(currentclass, cls).__new__(cls[, ...]) avec les arguments appropriés, puis en modifiant l'instance nouvellement créée au besoin avant de le retourner.

Si __new__() renvoie une instance de cls, puis __init__() de la nouvelle méthode d'instance sera appelée comme __init__(self[, ...]), où self est la nouvelle instance et les autres arguments sont les mêmes que ont été transmis à __new__().

Si __new__() ne retourne pas une instance de cls, puis méthode __init__() de la nouvelle instance ne sera pas invoquée.

__new__() vise principalement à permettre à sous-classes de immuables types (comme int, str ou tuple) pour personnaliser la création d'instance. Il est également souvent remplacé dans les métaclasses personnalisées afin de personnaliser la création de la classe .

Donc, en mg. De answer, l'ancien ne remet pas la fonction __init__ alors que les derniers appels fonctionnent __init__ après avoir appelé __new__.

+0

Comment 'type .__ new__' est-il lié à' object .__ new__'? Je n'ai pas eu cette partie. – Nishant

Questions connexes