2012-07-24 13 views
11

Estoy superando el método __new__() de una clase para devolver una instancia de clase que tiene un conjunto específico __init__(). Python parece llamar a la __init__() método proporcionado en su clase en lugar del método específico de la instancia, aunque la documentación de Python en¿Por qué Python no invoca el método de instancia __init __() en la creación de la instancia, sino que llama a la clase __init __()?

http://docs.python.org/reference/datamodel.html

dice:

implementaciones típicas crear una nueva instancia de la clase de invocando el método __new __() de la superclase usando super (currentclass, cls) .__ new __ (cls [, ...]) con los argumentos apropiados y luego modificando la instancia recién creada como necesaria antes de devolverla.

Si __new __() devuelve una instancia de cls, después de la nueva instancia __init __() método será invocado como __init __ (self [...]), donde el auto es la nueva instancia y las argumentos restantes son lo mismo que pasó a __new __().

Aquí está mi código de prueba:

#!/usr/bin/env python 

import new 

def myinit(self, *args, **kwargs): 
    print "myinit called, args = %s, kwargs = %s" % (args, kwargs) 


class myclass(object): 
    def __new__(cls, *args, **kwargs): 
     ret = object.__new__(cls) 

     ret.__init__ = new.instancemethod(myinit, ret, cls) 
     return ret 

    def __init__(self, *args, **kwargs): 
     print "myclass.__init__ called, self.__init__ is %s" % self.__init__ 
     self.__init__(*args, **kwargs) 

a = myclass() 

que da salida a

$ python --version 
Python 2.6.6 
$ ./mytest.py 
myclass.__init__ called, self.__init__ is <bound method myclass.myinit of <__main__.myclass object at 0x7fa72155c790>> 
myinit called, args =(), kwargs = {} 

Parece que la única manera de conseguir myinit() para correr, es llamarlo explícitamente como self.__init__()myclass.__init__() el interior.

+1

Muy buena pregunta! – Marcin

Respuesta

8

métodos especiales sobre las clases de nuevo estilo son considerados en el tipo de instancia, no en la instancia en sí. Esta es documented behaviour:

Para las clases de nuevo estilo, las invocaciones de métodos implícitos especiales sólo están garantizados para funcionar correctamente si está definida en un tipo de objeto, no en el diccionario de la instancia del objeto.Ese comportamiento es la razón por la cual el siguiente código genera una excepción (a diferencia del ejemplo equivalente, con clases de estilo antiguo):

>>> class C(object): 
...  pass 
... 
>>> c = C() 
>>> c.__len__ = lambda: 5 
>>> len(c) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: object of type 'C' has no len() 
8

Varios métodos especiales (incluido __init__, pero también sobrecargas del operador como __add__, etc.) son always accessed via the class rather than the instance. No solo eso, sino que no se puede acceder a través de un método __getattr__ o __getattribute__ en la clase o metaclase, tienen que estar en la clase directamente. Esto es por razones de eficiencia:

Sin pasar por la maquinaria __getattribute__() de esta manera proporciona un amplio margen para optimizaciones de velocidad dentro de la intérprete, a costa de una cierta flexibilidad en el manejo de métodos especiales (el método especial se debe establecer en el objeto de la clase en sí mismo para ser invocado constantemente por el intérprete).

No está del todo claro lo que estamos tratando de lograr, pero una cosa que podría hacer aquí es subclase myclass dentro del __new__ método:

class myclass(object): 
    def __new__(cls, *args, **kwargs): 
     class subcls(cls): 
      def __new__(cls, *args, **kwargs): 
       return object.__new__(cls) 
     subcls.__init__ = myinit 
     return subcls(*args, **kwargs) 
Cuestiones relacionadas