TL; DR:如果函数装饰器没有专门指定参数,装饰函数是否具有参数要求(在参数计数和kwarg名称方面)作为相同的函数未修饰?
我有一个用于处理所有购物车ajax调用的Web应用程序的控制器。在每次调用开始时,它初始化一个对象,该对象处理购物车功能的实际逻辑。 (这可能不是最有效的方法,但我的问题与实际的购物车位没有任何关系。)
代码如下所示:
from webapp import request
from shopping_cart import Cart
from decorators import decorator
@decorator
def init_cart(f, *args, **kwargs):
shopping_cart = Cart()
return f(shopping_cart = cart)
@init_cart
def add_item(shopping_cart = None):
shopping_cart.add(request.params)
@init_cart
def remove_item(shopping_cart = None):
shopping_cart.remove(request.params)
等等。
该代码存在于集中模块中,该模块由多个应用程序导入和调用。各个应用中的代码如下所示:
from sharedlib.controllers import cart
from app.base import *
class CartController(BaseController):
def index(self, url = None):
set_content_type('text/javascript')
controller_method = getattr(cart, url)
if controller_method:
return controller_method()
else:
abort(404)
我的问题如下:
如果我希望init_cart装饰器将它接收的参数传递给被调用的控制器方法,除了传递购物车模块之外,我会尝试:
@decorator
def init_cart(f, *args, **kwargs):
shopping_cart = Cart()
kwargs['shopping_cart'] = cart
return f(*args, **kwargs)
@init_cart
def add_item(shopping_cart = None):
shopping_cart.add(request.params)
但是我抛出了异常TypeError: add_item() got multiple values for keyword argument 'shopping_cart'
我不完全理解这种行为:kwargs肯定只有'shopping_cart'的一个键/值对。
此外,如果我试试这个:
@decorator
def init_cart(f, *args, **kwargs):
shopping_cart = Cart()
return f(cart)
@init_cart
def add_item(shopping_cart = None):
shopping_cart.add(request.params)
我收到错误TypeError: add_item() takes exactly 1 argument (0 given)
我假设我在某种程度上不了解装饰器在接收和传递参数时的行为 - 我假设如果init_cart
使用(*args, **kwargs)
则可以调用它装饰的函数任何一组参数,或缺乏参数。情况似乎并非如此。
此外,在前面的示例中,该方法如何为shopping_cart
参数接收多个值?
答案 0 :(得分:1)
def this_is_a_decorator(fn):
def decorated_fn(*args,**kwargs):
result = fn(*args,**kwargs) #notice we are calling the original function
return "%s decorated"%result
return decorated_fn
@this_is_a_decorator
def reverse_string(msg=""):
return msg[::-1]
print reverse_string("Hello!")
为你装饰
def init_cart(fn):
def decorated_fn(*args,**kwargs):
cart = Cart()
fn(cart)
return cart
return decorated_fn
@init_cart
def add_item(shopping_cart=None):
shopping_cart.add(request.params)
答案 1 :(得分:0)
我发现Joran在他的回答中是部分正确的:虽然我手工书写时对装饰器的理解并不正确,但我完全没理解decorator.decorator
装饰师在使用它时是做什么的创造它们。
这主要与decorator
模块如何尝试确定正在装饰的函数的argspec有关。演示此行为的最简单方法是将使用@decorator.decorator
制作的装饰器与手动制作的装饰器进行比较(为了实验目的,传递另一个对象已被替换为传递字符串):
import decorator
@decorator.decorator
def init_cart(f, *args, **kwargs):
kwargs['shopping_cart'] = 'cart'
print args, kwargs
return f(*args, **kwargs)
@init_cart
def add_item(shopping_cart = None):
print shopping_cart
def init_cart2(f):
def wrapped(*args, **kwargs):
kwargs['shopping_cart'] = 'cart'
print args, kwargs
return f(*args, **kwargs)
return wrapped
@init_cart2
def add_item2(shopping_cart = None):
print shopping_cart
因此add_item2
将始终使用手动装饰器,add_item
将使用decorator
模块中的帮助程序。
我原本预计在两种情况下调用add_item(shopping_cart = 'some value')
都会导致打印'cart'
(在这两种情况下,参数都是关键字参数,由装饰函数覆盖)。相反,以下结果(两个装饰器打印它们发送到包装函数的参数):
>>> add_item(shopping_cart = 'some_value')
('some_value',) {'shopping_cart': 'cart'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 2, in add_item
File "<stdin>", line 5, in init_cart
TypeError: add_item() got multiple values for keyword argument 'shopping_cart'
和
>>> add_item2(shopping_cart = 'some_value')
() {'shopping_cart': 'cart'}
cart
在第一个示例中,init_cart
未接收参数作为关键字参数。
查看decorator
模块的代码,为什么会这样更清楚:
def decorator(caller, func=None):
"""
decorator(caller) converts a caller function into a decorator;
decorator(caller, func) decorates a function using a caller.
"""
if func is None: # returns a decorator
fun = FunctionMaker(caller)
first_arg = inspect.getargspec(caller)[0][0]
src = 'def %s(%s): return _call_(caller, %s)' % (
caller.__name__, first_arg, first_arg)
return fun.make(src, dict(caller=caller, _call_=decorator),
undecorated=caller)
else: # returns a decorated function
fun = FunctionMaker(func)
src = """def %(name)s(%(signature)s):
return _call_(_func_, %(signature)s)"""
return fun.make(src, dict(_func_=func, _call_=caller), undecorated=func)
看起来包裹的init_cart
函数没有在精确中调用,就像调用包装器一样,因此我感到困惑。
事后看来,这更有意义,而且很可能是因为我使用了一个我不太了解的行为帮助。