2017-10-02 4 views
0

Je cherche à faire ce qui suit:Python - Passer la classe en tant qu'argument au décorateur de la méthode dans cette classe?

class A(object): 
    def first_method(self): 
     print "I'm the first method!"  

    @some_decorator(A.first_method) 
    def second_method(self): 
     print "I'm the second method!" 

Mais je suis en cours d'exécution dans le problème A n'est pas défini en lui-même au moment où le décorateur est analysé. Y at-il un moyen pour moi de référence A dans le décorateur? Alternativement, si je viens de passer le décorateur la méthode liée first_method est-il possible pour moi de récupérer que first_method appartient à A?

+0

Je pense que ce post petit blog pourrait être utile: http://scottlobdell.me/2015/04/decorators-arguments-python/ – freddiev4

+0

@ FreddieV4, je suis conscient de la façon de créer un décorateur avec des arguments. Le problème, comme indiqué, est que «A» n'est pas défini en lui-même. Le fait de référencer 'first_method' depuis' A' mais en dehors d'une fonction retournera la méthode non liée 'first_method', pas' A.first_method'. –

Répondre

1

En python 3, vous pouvez juste dire @some_decorator(first_method), et cela fonctionnera, comme toutes les méthodes sont des fonctions normales dans les classes comme les conteneurs.

En python 2, il existe un système compliqué de & méthodes non liées, instance, classe et statiques. Pour cette raison, vous ne pouvez pas accéder à first_method à l'intérieur d'une définition de classe m (jusqu'à ce que la classe soit complètement formée).

Une petite solution serait de diviser cette classe en deux classes:

class BaseA(object): 
    def first_method(self): 
     print "I'm the first method!"  

class A(BaseA): 
    @some_decorator(BaseA.first_method) 
    def second_method(self): 
     print "I'm the second method!" 

pas la meilleure solution pour tous les cas, mais fonctionnera.


De plus, gardez à l'esprit que dans les deux cas (py2 & AP3), le décorateur fera référence à la first_method comme il a été déclaré ici. Si une classe descendante redéfinit la méthode, la nouvelle méthode ne sera PAS utilisée dans le décorateur; seul le parent sera utilisé.

Probablement, vous ne devriez pas du tout faire référence à first_method. Au lieu de cela, acceptez self/cls premier argument de position dans l'enveloppe du décorateur, et utiliser self.first_method/cls.first_method il:

import functools 

def some_decorator(fn): 
    @functools.wraps(fn) 
    def wrapper(self, *args, **kwargs): 
     first_method = self.first_method 
     first_method() 
     return fn(self, *args, **kwargs) 
    return wrapper 

class A(object): 
    def first_method(self): 
     print "I'm the first method of A!"  

    @some_decorator 
    def second_method(self): 
     print "I'm the second method!" 


class B(A): 
    def first_method(self): 
     print "I'm the first method of B!"  


A().second_method() 
# I'm the first method of A! 
# I'm the second method! 

B().second_method() 
# I'm the first method of B! 
# I'm the second method! 

Si vous voulez faire cette méthode configurable:

def some_decorator(method_name): 
    def decorator(fn): 
     @functools.wraps(fn) 
     def wrapper(self, *args, **kwargs): 
      first_method = getattr(self, method_name) 
      first_method() 
      return fn(self, *args, **kwargs) 
     return wrapper 
    return decorator 


class A(object): 
    def first_method(self): 
     print "I'm the first method of A!"  

    @some_decorator('first_method') 
    def second_method(self): 
     print "I'm the second method!" 

class B(A): 
    def first_method(self): 
     print "I'm the first method of B!"  
+0

Ce dernier bit, il y a la réponse mate! Avoir le wrapper accepte le self permettra à la classe de base d'être capturée! Des trucs géniaux. Merci. –

0

Vous pouvez utiliser votre décorateur en fonction classique (sans le @):

def some_decorator(arg): 
    # ... 
    pass 


class A(object): 
    def first_method(self): 
     print("I'm the first method!") 

    def second_method(self): 
     print("I'm the second method!") 


A.second_method = some_decorator(A.first_method)(A.second_method)