2012-07-01 3 views
4

Dire qu'il ya:Python méthodes de classe dynamique

class A(B): 
    ... 

B pourrait être object et ... est pas:

@classmethod # or @staticmethod 
def c(cls): print 'Hello from c!' 

Que dois-je faire appel A.c() déclencheur wont AttributeError? En d'autres termes, je sais qu'il est possible d'ajouter manuellement des méthodes de classe à une classe au moment de l'exécution. Mais est-il possible de le faire automatiquement, dire à chaque fois qu'une méthode de classe manque, cela crée une méthode fictive?

Dans un autre mots, que si je pouvais remplacer A.__dict__ avec mon dict qui gère __getitem__ - mais A.__dict__ semble ne pas être accessible en écriture ...

Répondre

11

Vous pouvez le faire en utilisant un __getattr__ hook sur un metaclass.

class DefaultClassMethods(type): 
    def __getattr__(cls, attr): 
     def _defaultClassMethod(cls): 
      print 'Hi, I am the default class method!' 
     setattr(cls, attr, classmethod(_defaultClassMethod)) 
     return getattr(cls, attr) 

Démo:

>>> class DefaultClassMethods(type): 
...  def __getattr__(cls, attr): 
...   def _defaultClassMethod(cls): 
...    print 'Hi, I am the default class method!' 
...   setattr(cls, attr, classmethod(_defaultClassMethod)) 
...   return getattr(cls, attr) 
... 
>>> class A(object): 
...  __metaclass__ = DefaultClassMethods 
... 
>>> A.spam 
<bound method DefaultClassMethods._defaultClassMethod of <class '__main__.A'>> 
>>> A.spam() 
Hi, I am the default class method! 

Notez que nous avons mis le résultat de la ligne droite appel classmethod sur la classe, il cache efficacement pour les recherches futures.

Si vous avez besoin de régénérer la méthode de classe à chaque appel à la place, utiliser le même method to bind a function to an instance mais avec la classe à la place et métaclasse (en utilisant cls.__metaclass__ être compatible avec métaclasse subclassing):

from types import MethodType 

class DefaultClassMethods(type): 
    def __getattr__(cls, attr): 
     def _defaultClassMethod(cls): 
      print 'Hi, I am the default class method!' 
     return _defaultClassMethod.__get__(cls, cls.__metaclass__) 

Pour les méthodes statiques juste retourner la fonction directement dans tous les cas, pas besoin de muck avec le décorateur staticmethod ou le protocole descripteur.

+0

A.asd() - AttributeError: « objet de type 'A' a pas d'attribut 'asd' " –

+0

@EcirHana: ah, vous voulez dire une méthode de classe par défaut, va mettre à jour. –

+0

Je souhaite que le spam soit une méthode de classe, pas une méthode d'instance. –

0
>>> class C(object): 
...  pass 
... 
>>> C.m = classmethod(lambda cls: cls.__name__) 
>>> C.m() 
'C' 

Ou vous pouvez utiliser somethigs comme ceci:

class Wrapper(object): 

    def __init__(self, clz, default=lambda cls: None): 
     self._clz = clz 
     self._default = default 

    def __getattr__(self, attr): 
     # __attrs__ will be getted from Wrapper 
     if attr.startswith('__'): 
      return self.__getattribute__(attr) 

     if not hasattr(self._clz, attr): 
      setattr(self._clz, attr, classmethod(self._default)) 
     return getattr(self._clz, attr) 

    def __call__(self, *args, **kwargs): 
     return self._clz(*args, **kwargs) 

>>> class C(object): 
...  pass 
... 
>>> C = Wrapper(C, default=lambda cls: cls.__name__) 
>>> c = C() 
>>> print C.m() 
'C' 
>>> print c.m() # now instance have method "m" 
'C' 
+0

Merci. Oui, je sais que vous pouvez ajouter des méthodes de classe manuellement. Y a-t-il un moyen de le faire automatiquement? –

+0

@EcirHana, python instancie les objets de classe lors de l'importation du module. Et ces objets de classe n'ont pas la méthode '__getattribute__' ou un équivalent ... – astynax

+0

@EcirHana, j'ai essayé de mettre en place une sorte de wrapper ... – astynax

1

Les comportements fournis aux instances par des méthodes telles que __getattr__ et le protocole de descripteur peut fonctionner pour les classes aussi bien, mais dans ce cas, vous devez codez-les dans la métaclasse de la classe.

Dans ce cas, tout ce que vous devez faire est de définir la métaclasse __getattr__ pour générer automatiquement l'attribut de classe désiré.

(Le setattr, tour getattr est de laisser Python faire le Fonction-> méthode transoform sans avoir besoin de salir avec elle)

class AutoClassMethod(type): 
    def __getattr__(cls, attr): 
     default = classmethod(lambda cls: "Default class method for " + repr(cls)) 
     setattr(cls, attr, default) 
     return getattr(cls, attr) 



class A(object): 
    __metaclass__ = AutoClassMethod 
    @classmethod 
    def b(cls): 
     print cls 
Questions connexes