关于如何通过Python装饰器传递参数的困惑

迈克尔·舒勒(Michael Schuller)

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参数接收多个值

迈克尔·舒勒(Michael Schuller)

我发现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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何理解python装饰器参数传递

来自分类Dev

Python装饰器。传递参数时出错

来自分类Dev

某些函数参数可以通过装饰器传递吗?

来自分类Dev

如何使用带参数的python装饰器?

来自分类Dev

如何将非硬编码参数传递给Python装饰器?

来自分类Dev

如何将非硬编码参数传递给Python装饰器?

来自分类Dev

装饰器:参数如何传递给包装函数?

来自分类Dev

如何将参数传递给类中的装饰器

来自分类Dev

如何通过 Python 中的装饰器检查参数和返回类型

来自分类Dev

如何通过装饰器获取底层函数参数信息?

来自分类Dev

使用装饰器模块将参数传递给装饰器

来自分类Dev

Python 中关于装饰器的错误

来自分类Dev

如何在Python的类装饰器中传递args?

来自分类Dev

将参数传递给装饰器函数

来自分类Dev

将参数传递给类装饰器

来自分类Dev

Python装饰器提取参数

来自分类Dev

带参数的 Python 装饰器

来自分类Dev

关于varargs参数的困惑

来自分类Dev

调用装饰函数时将参数传递给装饰器

来自分类Dev

如何使python装饰器中的参数可配置?

来自分类Dev

Python:如何将变量传递到装饰器中,并且装饰器也将装饰器变量传递回函数中?

来自分类Dev

如何在TypeScript中将其他参数传递给属性装饰器?

来自分类Dev

2.0中的气流dag和任务装饰器:如何将配置参数传递给任务?

来自分类Dev

从装饰器传递位置参数时,如何支持静态和类方法?

来自分类Dev

如何将类变量传递给参数化装饰器

来自分类Dev

如何使用python在perforce中创建分支并避免编辑器通过参数传递映射

来自分类Dev

在传递的类中使用时,允许python装饰器将类作为参数

来自分类Dev

Python3无法将@property作为装饰器参数传递

来自分类Dev

关于原始查询参数的困惑

Related 相关文章

  1. 1

    如何理解python装饰器参数传递

  2. 2

    Python装饰器。传递参数时出错

  3. 3

    某些函数参数可以通过装饰器传递吗?

  4. 4

    如何使用带参数的python装饰器?

  5. 5

    如何将非硬编码参数传递给Python装饰器?

  6. 6

    如何将非硬编码参数传递给Python装饰器?

  7. 7

    装饰器:参数如何传递给包装函数?

  8. 8

    如何将参数传递给类中的装饰器

  9. 9

    如何通过 Python 中的装饰器检查参数和返回类型

  10. 10

    如何通过装饰器获取底层函数参数信息?

  11. 11

    使用装饰器模块将参数传递给装饰器

  12. 12

    Python 中关于装饰器的错误

  13. 13

    如何在Python的类装饰器中传递args?

  14. 14

    将参数传递给装饰器函数

  15. 15

    将参数传递给类装饰器

  16. 16

    Python装饰器提取参数

  17. 17

    带参数的 Python 装饰器

  18. 18

    关于varargs参数的困惑

  19. 19

    调用装饰函数时将参数传递给装饰器

  20. 20

    如何使python装饰器中的参数可配置?

  21. 21

    Python:如何将变量传递到装饰器中,并且装饰器也将装饰器变量传递回函数中?

  22. 22

    如何在TypeScript中将其他参数传递给属性装饰器?

  23. 23

    2.0中的气流dag和任务装饰器:如何将配置参数传递给任务?

  24. 24

    从装饰器传递位置参数时,如何支持静态和类方法?

  25. 25

    如何将类变量传递给参数化装饰器

  26. 26

    如何使用python在perforce中创建分支并避免编辑器通过参数传递映射

  27. 27

    在传递的类中使用时,允许python装饰器将类作为参数

  28. 28

    Python3无法将@property作为装饰器参数传递

  29. 29

    关于原始查询参数的困惑

热门标签

归档