Python: Почему я не могу заставить моего декоратора работать? - PullRequest
2 голосов
/ 11 января 2010

Теперь это работает для новичков в этом вопросе:

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

Ответы [ 3 ]

2 голосов
/ 11 января 2010

Декораторы обычно выглядят так:

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

Затем они работают с любой переданной им функцией, а не только с определенным числом аргументов или с определенными аргументами ключевых слов. В этом конкретном случае вам может потребоваться выполнить некоторый самоанализ для функции, переданной в __call__, чтобы выяснить, является ли это функцией с одним или двумя аргументами, и убедиться, что последний аргумент называется «params». Тогда просто напишите wrapper вот так:

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)
2 голосов
/ 11 января 2010

def wrapper(params): означает, что вы принимаете только один аргумент - и поэтому, конечно, вызовы с (self, params) просто не будут работать. Вы должны быть в состоянии принять один или два аргумента, например, как минимум (если вам не нужно поддерживать вызовы с именованными аргументами):

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

Вы можете сделать намного более сложным / сложным, чтобы также принимать именованные аргументы, но это намного проще и все еще "в основном работает"; -).

0 голосов
/ 11 января 2010

Что я сделал, так это добавил * args, ** kwargs и просто проверил наличие ключей, требуемых в аргументе 'params', через kwargs ['params'] после проверки существования параметров kwargs.

Вот новая версия (которая отлично работает):

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()
...