Python:为什么我不能让我的装饰工作?

时间:2010-01-11 03:08:03

标签: python decorator

现在适用于此问题的新手:

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 个答案:

答案 0 :(得分:2)

def wrapper(params):意味着你只会接受一个论点 - 当然,使用(self, params)的电话不会起作用。你需要能够接受一个或两个参数,例如,至少(如果你不需要支持名为args的调用):

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

为了也接受命名参数,你可以得到更复杂/更复杂的东西,但这更简单并且仍然“主要起作用”; - )。

答案 1 :(得分:2)

装饰器通常如下所示:

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

然后它们使用传递给它们的任何函数,而不仅仅是具有特定数量的参数或具有特定关键字参数的函数。在这种特定情况下,您可能希望对传递给__call__的func进行一些内省,以确定它是一个或两个参数函数,并确保最后一个参数被称为'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 :(得分:0)

我做的是添加* args,** kwargs,并在检查kwargs params存在后,通过kwargs ['params']检查'params'参数中所需的键。

这是新版本(完美运行):

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