2010-01-11 6 views
2

Cela fonctionne maintenant pour les nouveaux à cette question:Python: Pourquoi je n'arrive pas à faire fonctionner mon décorateur?

class ensureparams(object): 
    """ 

    Used as a decorator with an iterable passed in, this will look for each item 
    in the iterable given as a key in the params argument of the function being 
    decorated. It was built for a series of PayPal methods that require 
    different params, and AOP was the best way to handle it while staying DRY. 


    >>> @ensureparams(['name', 'pass', 'code']) 
    ... def complex_function(params): 
    ...  print(params['name']) 
    ...  print(params['pass']) 
    ...  print(params['code']) 
    >>> 
    >>> params = { 
    ...  'name': 'John Doe', 
    ...  'pass': 'OpenSesame', 
    ...  #'code': '1134', 
    ... } 
    >>> 
    >>> complex_function(params=params) 
    Traceback (most recent call last): 
     ... 
    ValueError: Missing from "params" dictionary in "complex_function": code 
    """ 
    def __init__(self, required): 
     self.required = set(required) 

    def __call__(self, func): 
     def wrapper(*args, **kwargs): 
      if not kwargs.get('params', None): 
       raise KeyError('"params" kwarg required for {0}'.format(func.__name__)) 
      missing = self.required.difference(kwargs['params']) 
      if missing: 
       raise ValueError('Missing from "params" dictionary in "{0}": {1}'.format(func.__name__, ', '.join(sorted(missing)))) 
      return func(*args, **kwargs) 
     return wrapper 

if __name__ == "__main__": 
    import doctest 
    doctest.testmod() 

Répondre

0

Ce que j'ai été ajouter * args, ** kwargs, et il suffit de cocher les clés nécessaires au sein de l'argument des «params de par kwargs après vérification [ « params »] que kwargs params existe.

est ici la nouvelle version (qui fonctionne parfaitement):

class requiresparams(object): 
    """ 

    Used as a decorator with an iterable passed in, this will look for each item 
    in the iterable given as a key in the params argument of the function being 
    decorated. It was built for a series of PayPal methods that require 
    different params, and AOP was the best way to handle it while staying DRY. 


    >>> @requiresparams(['name', 'pass', 'code']) 
    ... def complex_function(params): 
    ...  print(params['name']) 
    ...  print(params['pass']) 
    ...  print(params['code']) 
    >>> 
    >>> params = { 
    ...  'name': 'John Doe', 
    ...  'pass': 'OpenSesame', 
    ...  #'code': '1134', 
    ... } 
    >>> 
    >>> complex_function(params=params) 
    Traceback (most recent call last): 
     ... 
    ValueError: Missing from "params" dictionary: code 
    """ 
    def __init__(self, required): 
     self.required = set(required) 

    def __call__(self, func): 
     def wrapper(*args, **kwargs): 
      if not kwargs.get('params', None): 
       raise KeyError('"params" kwarg required for {0}'.format(func.__name__)) 
      missing = self.required.difference(kwargs['params']) 
      if missing: 
       raise ValueError('Missing from "params" dictionary: %s' % ', '.join(sorted(missing))) 
      return func(*args, **kwargs) 
     return wrapper 

if __name__ == "__main__": 
    import doctest 
    doctest.testmod() 
2

def wrapper(params): signifie que vous allez seulement accepter un argument - et donc bien sûr appels avec (self, params) ne va pas fonctionner. Vous devez être en mesure d'accepter un ou deux arguments, par exemple, à tout le moins (si vous n'avez pas besoin de soutenir les appels w/named args):

def wrapper(one, two=None): 
    if two is None: params = one 
    else: params = two 
    # and the rest as above 

Vous pouvez obtenir beaucoup plus complexe/sophistiqué afin d'accepter aussi les arguments nommés, mais cela est beaucoup plus simple et fonctionne toujours "; surtout" ;-).

+0

Oh, non non. 'params' est le nom d'un argument du dictionnaire. Cela prend juste un argument. Voir le doctest inclus. – orokusaki

+0

@orokusaki, lorsque vous écrivez 'def foo (self, params):', ce sont des arguments ** DEUX **, pas un - comptez-les. Votre échec à reconnaître ceci est ce qui cause le bug dans votre code: vous devez accepter d'être appelé avec un ou deux args, et votre 'def wrapper', comme codé, ne le fait pas (cela force un un seul argument, juste comme vous pensez qu'il devrait ... et vous avez tort, Python vous le dit, et moi aussi ;-). –

+0

Avez-vous vu ma question Alex? Comment puis-je obtenir le décorateur de travailler pour une méthode d'une classe et une fonction normale sans avoir deux versions du décorateur. Je ne suis pas un noob complet. Je ne comprends pas comment faire une version par exemple des méthodes et des fonctions. – orokusaki

2

Décorateurs regardent normalement comme ceci:

def wrapper(*args, **kargs): 
    # Pull what you need out of the argument lists and do stuff with it 
    func(*args, **kargs) 

Ensuite, ils travaillent avec une fonction passée à eux, non seulement les fonctions avec un certain nombre d'arguments ou des arguments de mots-clés spécifiques. Dans ce cas précis, vous pouvez faire une introspection sur la fonction passée à __call__ pour savoir s'il s'agit d'une fonction à un ou deux arguments et pour vous assurer que le dernier argument est appelé 'params'. Ensuite, il suffit d'écrire wrapper comme ceci:

def wrapper(*args): 
    params = args[-1] 
    missing = self.required.difference(params) 
    if missing: 
     raise ValueError('Missing from "params" argument: %s' % ', '.join(sorted(missing))) 
    func(params) 
+0

Merci Omnifarious. J'ai fini par utiliser * args, ** kwargs à la place mais ta réponse m'a fait réfléchir dans la bonne direction. – orokusaki

Questions connexes