2012-07-24 18 views
11

Ho superato il metodo __new__() di una classe per restituire un'istanza di classe che ha uno specifico set __init__(). Python sembra chiamare il __init__() metodo della classe fornita invece del metodo specifico per esempio, anche se la documentazione Python aPerché Python non chiama il metodo istanza __init __() sulla creazione dell'istanza ma chiama __init __() fornita dalla classe?

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

dice:

implementazioni tipiche creare una nuova istanza della classe invocando il metodo __new __() della superclasse usando super (currentclass, cls) .__ new __ (cls [, ...]) con gli argomenti appropriati e quindi modificando l'istanza appena creata come necessaria prima di restituirla.

Se __new __() restituisce un'istanza di cls, allora il nuovo dell'istanza __init __() Metodo verrà richiamato come __init __ (self [, ...]), dove l'auto è la nuova istanza e gli restanti argomenti sono lo stesso che è stato passato a __new __().

Ecco il mio codice di prova:

#!/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() 

che emette

$ 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 = {} 

Sembra che l'unico modo per ottenere myinit() a correre, è quello di chiamare esplicitamente come self.__init__() all'interno myclass.__init__().

+1

grande domanda! – Marcin

risposta

8

metodi speciali su classi di nuovo stile sono guardato sul tipo di istanza, non sull'istanza stessa. Questo è documented behaviour:

Per classi di nuovo stile, invocazioni implicite di metodi speciali sono garantiti solo per funzionare correttamente se definito dal tipo di un oggetto, non nel dizionario istanza dell'oggetto.Questo comportamento è il motivo per cui il seguente codice solleva un'eccezione (a differenza l'esempio equivalente con le classi vecchio stile):

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

Vari metodi speciali (incluso __init__, ma anche sovraccarichi dell'operatore come __add__, ecc.) Sono always accessed via the class rather than the instance. Non solo, ma non è possibile accedervi tramite un metodo __getattr__ o __getattribute__ sulla classe o sulla metaclasse, devono essere direttamente nella classe. Questo è per ragioni di efficienza:

Ignorando il macchinario __getattribute__() in questo modo fornisce un ampio margine di ottimizzazioni velocità comprese l'interprete, a costo di una certa flessibilità nella gestione dei metodi speciali (metodo speciale deve essere impostato l'oggetto classe stesso per essere invocato in modo coerente dall'interprete).

Non è del tutto chiaro che cosa si sta cercando di realizzare, ma una cosa che si possa fare è quello di creare una sottoclasse myclass all'interno del metodo __new__:

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)