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装饰器将其收到的参数传递给被调用的控制器方法,则除了传递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
参数接收多个值?
我发现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
函数没有被调用正是因为它的包装是被称为以同样的方式,所以我的困惑。
事后看来,这更有意义,这也许是我使用一个我不太了解其行为的助手所得到的。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句