2010-01-11 4 views
2

Это теперь работает для тех, кто новичок в этом вопросе:Python: Почему я не могу заставить мой декоратор работать?

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

ответ

0

То, что я сделал, это добавить * арг, ** kwargs, и просто проверить ключи, которые необходимы в аргументе «Params» через kwargs [ «Params»] после проверки что kwargs 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() 
2

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

О, нет, нет. «params» - это имя словарного аргумента. Это требует только одного аргумента. См. Включенный комментарий. – orokusaki

+0

@orokusaki, когда вы пишете 'def foo (self, params):', это ** TWO ** аргументы, а не один - считать их. Ваш отказ от подтверждения этого - это то, что вызывает ошибку в вашем коде: вы ** имеете **, чтобы принять вызов одним или двумя аргументами, а ваша 'def wrapper', как закодированная, просто не делает (это вынуждает единственный аргумент, как вы думаете, это должно ... и вы ошибаетесь, Python говорит вам это, и я тоже ;-). –

+0

Вы видели мой вопрос Алекс? Как заставить декоратор работать для метода класса и нормальной функции без наличия двух версий декоратора. Я не полный нуб. Я просто не понимаю, как сделать версию, например, методами и функциями. – orokusaki

2

Декораторы обычно выглядит следующим образом:

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

Затем они работают с любой функцией, переданной им, а не только функции с определенным количеством аргументов или с конкретными аргументами ключевых слов. В этом конкретном случае вы можете сделать некоторую интроспекцию в func, переданном __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) 
+0

Спасибо, Omnifarious. В итоге я использовал * args, ** kwargs, но ваш ответ заставил меня думать в правильном направлении. – orokusaki

Смежные вопросы