2012-07-24 1 views
11

J'applique la méthode __new__() d'une classe pour renvoyer une instance de classe qui a un ensemble spécifique __init__(). Python semble appeler la méthode de classe fourni __init__() au lieu de la méthode spécifique à l'instance, bien que la documentation Python àPourquoi Python n'appelle-t-il pas la méthode d'instance __init __() lors de la création de l'instance mais appelle plutôt __init __() à la place?

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

dit:

implémentations typiques créer une nouvelle instance de la classe par invoquant la 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 comme avant de la renvoyer.

Si __new __() retourne une instance de cls, la nouvelle méthode __ __init de l'instance() sera invoqué comme __init __ (self [...]), où l'auto est la nouvelle instance et les arguments restants sont les mêmes que ceux qui ont été transmis à __new __().

Voici mon code de test:

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

qui sort

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

Il semble que le seul moyen d'obtenir myinit() pour exécuter, est d'appeler explicitement comme self.__init__() l'intérieur myclass.__init__().

+1

Grande question! – Marcin

Répondre

8

méthodes spéciales sur les classes de type nouveau sont regardés sur le type de l'instance, pas sur l'instance elle-même. Ceci est documented behaviour:

Pour les classes de type nouveau, les invocations implicites des méthodes spéciales ne sont garantis pour fonctionner correctement si elle est définie sur le type d'un objet, pas dans le dictionnaire de l'instance de l'objet.Ce comportement est la raison pour laquelle le code suivant soulève une exception (contrairement à l'exemple équivalent avec les classes de style ancien):

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

Diverses méthodes spéciales (y compris __init__, mais aussi les surcharges de l'opérateur telles que __add__, etc.) sont always accessed via the class rather than the instance. Non seulement cela, mais ils ne peuvent pas être accessibles via une méthode __getattr__ ou __getattribute__ sur la classe ou la métaclasse, ils doivent être sur la classe directement. Ceci est pour des raisons d'efficacité:

Contourner les machines __getattribute__() de cette façon fournit d'importantes possibilités pour Optimisations de vitesse au sein de l'interprète, au prix d'une certaine flexibilité dans la gestion des méthodes spéciales (la méthode spéciale doit être sur l'objet de classe lui-même afin d'être invoqué de manière cohérente par l'interprète).

Ce n'est pas tout à fait clair ce que vous essayez d'accomplir, mais une chose que vous pouvez faire ici est de sous-classe myclass dans la méthode __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) 
Questions connexes