2010-04-09 21 views
11

estaba escribiendo una metaclase, y sin querer hice así:¿Cuál es la diferencia entre tipo y tipo .__ new__ en python?

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

... en lugar de la siguiente manera:

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

Cuál es exactamente la diferencia entre estos dos metaclases? Y más específicamente, ¿qué causó que el primero no funcionara correctamente (la metaclass no llamó a algunas clases)?

Respuesta

6

En el primer ejemplo que está creando toda una clase nueva:

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

mientras que en el segundo caso que está llamando de los padres __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

Todo se describe bastante bien here.

Si no devuelve el tipo correcto de objeto, no tiene sentido definir una metaclass personalizada.

+1

Pero esa es la pregunta que estoy recibiendo: ¿por qué 'type' no devuelve el tipo correcto de objeto? Sé que tengo que usar el método '__new__' de la subclase, pero ¿cómo es eso diferente de usar' type'? –

+1

¿Cómo se supone que el tipo sabe mágicamente qué tipo de objeto quieres que haga? –

+0

bueno, porque yo lo digo. Pasé en nombre, bases y dict. ¿Qué otra información necesito para darle? –

3
return type(name, bases, dict) 

Lo que se obtiene de esta es una nueva type, y no una instancia MetaCls en absoluto. En consecuencia, sus métodos definidos en MetaCls (incluido __init__) no se pueden llamar.

type.__new__ se llamará como parte de la creación de ese nuevo tipo, sí, pero el valor de cls entrar en esa función va a ser type y no 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' 

Otra forma de lograr el mismo resultado:

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

MyMeta es invocable, por lo que Python utilizará el método especial __call__.

Python buscará __call__ en el MyMeta 's tipo (que es type en nuestro caso)

"Para las clases de nuevo estilo, invocaciones implícitas de métodos especiales están sólo se garantiza que funcionen correctamente si se define en el tipo de un objeto, no en el diccionario de la instancia del objeto"

MyClass = MyMeta(...) se interpreta como:

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

Dentro de la type.__call__() me imagino algo como esto:

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

MyMeta.__new__() decidirá cómo se construye el MyClass:

type.__new__(meta, cls, bases, attributes) establecerá la metaclase correcta (que es MyMeta) para MyClass

type(cls, bases, attributes) configurará la metaclase predeterminada (que es tipo) para MyClass

4

Por favor, consulte la anotación a continuación, espero que esto sea útil.

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

Lo primero que hay que averiguar es cómo object.__new__() obras.

Aquí es desde el documentation:

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

llama para crear una nueva instancia de la clase cls. __new__() es un método estático (con cassette especial por lo que no necesita declararlo como tal) que toma la clase de la cual se solicitó una instancia como primer argumento. Los argumentos restantes son aquellos pasados ​​a la expresión del constructor de objetos (la llamada a la clase). El valor de retorno de __new__() debe ser la nueva instancia de objeto (generalmente una instancia de cls).

implementaciones típicas crear una nueva instancia de la clase invocando método de la superclase __new__() usando super(currentclass, cls).__new__(cls[, ...]) con argumentos apropiados y luego modificando la instancia recién creado como sea necesario antes de devolverla.

Si __new__() devuelve una instancia de cls, a continuación, el método de la nueva instancia __init__() se invocará como __init__(self[, ...]), donde self es la nueva instancia y el resto de argumentos son los mismos que se pasaron a __new__().

Si __new__() no devuelve una instancia de cls, entonces el método de la nueva instancia __init__() no será invocado.

__new__() está destinado principalmente para permitir subclases de inmutables tipos (como int, str, o tuple) para personalizar la creación de la instancia. También es anulado comúnmente en metaclases personalizadas para personalizar la creación de clase .

Así en mg de answer., El primero no llamada a la función __init__ mientras que el segundo llamadas a funciones __init__ después de llamar __new__.

+0

¿Cómo se relaciona 'type .__ new__' con' object .__ new__'? No entendí esa parte. – Nishant

Cuestiones relacionadas