2017-02-16 1 views
3

J'essaie de faire des validations pour les méthodes de classe d'une classe en utilisant l'un des paramètres utilisés lors de leur appel. Pour ce faire, j'utilise un décorateur pour la classe qui appliquera un décorateur aux méthodes requises, qui exécuteront une fonction de validation en utilisant l'un des paramètres de la fonction.comportement cls dans la méthode de classe héritée d'une classe décorée

Tout cela fonctionne bien pour la classe de base (pour cet exemple, je l'appellerai Parent).

Cependant, si je fais une autre classe qui hérite Parent, (pour cet exemple je l'appellerai Child), la méthode de classe décorée héritée ne se comporte plus normalement.

Le paramètre cls à l'intérieur du classmethod pour la classe Child n'est pas Child comme prévu, mais est Parent à la place.

En prenant l'exemple suivant

import inspect 


def is_number(word): 
    if word.isdigit(): 
     print('Validation passed') 
    else: 
     raise Exception('Validation failed') 


class ClassDecorator(object): 

    def __init__(self, *args): 
     self.validators = args 

    def __decorateMethod(self): 
     def wrapped(method): 
      def wrapper(cls, word, *args, **kwargs): 
       for validator in self.validators: 
        validator(word) 
       return method(word, *args, **kwargs) 
      return wrapper 
     return wrapped 

    def __call__(self, cls): 
     for name, method in inspect.getmembers(cls): 
      if name == 'shout': 
       decoratedMethod = self.__decorateMethod()(method) 
       setattr(cls, name, classmethod(decoratedMethod)) 
     return cls 


@ClassDecorator(is_number) 
class Parent(object): 

    @classmethod 
    def shout(cls, word): 
     print('{} is shouting {}'.format(cls, word)) 

    @classmethod 
    def say(cls): 
     print('{} is talking'.format(cls)) 


class Child(Parent): 
    pass 


Parent.shout('123') 
Child.shout('321') 

entraînera la sortie suivante:

Validation passed 
<class '__main__.Parent'> is shouting 123 
Validation passed 
<class '__main__.Parent'> is shouting 321 

Mes questions sont les suivantes:

  • Pourquoi le classmethod pour Child être appelée avec Parent comme cls
  • Est-il possible d'utiliser ce design pour obtenir le comportement voulu?

P.S.: J'ai essayé cette fois sur Python 2.7.10 et Python 3.5.2 et ont obtenu le même comportement

Répondre

5

Vous décorez la méthode de classe liée ; c'est cet objet qui conserve Parent et le passe dans la fonction shout d'origine lorsqu'il est appelé; quoi que ce soit cls est lié à dans votre wrapper() méthode n'est pas transmise et ignoré.

Déballer classmethods d'abord, vous pouvez accéder à l'objet de la fonction sous-jacente avec l'attribut __func__:

def __call__(self, cls): 
    for name, method in inspect.getmembers(cls): 
     if name == 'shout': 
      decoratedMethod = self.__decorateMethod()(method.__func__) 
      setattr(cls, name, classmethod(decoratedMethod)) 
    return cls 

Vous devez maintenant prendre en compte que votre emballage gère une trop fonction non liée , donc passer l'argument cls ou lier manuellement:

# pass in cls explicitly: 
return method(cls, word, *args, **kwargs) 

# or bind the descriptor manually: 
return method.__get__(cls)(word, *args, **kwargs) 
+0

Cela a fonctionné! Merci pour l'aide – Oct