【问题标题】:‘kwargs’ is empty in python decoratorpython 装饰器中的“kwargs”为空
【发布时间】:2019-02-06 15:01:40
【问题描述】:

我在下面运行了一个装饰器演示。

def logger(func):
    def inner(*args, **kwargs):
        print(args)
        print(kwargs)
        return func(*args, **kwargs)
    return inner

@logger
def foo1(a, b, c, x=2, y=1):
    print(x * y)

foo1(6,7,8)

输出是:

(6, 7, 8)
{}
2

为什么字典是空的?我觉得应该是{'x':2, 'y':1}

【问题讨论】:

  • 这个问题在这里有答案:stackoverflow.com/questions/31728346/… 但由于赏金,我无法标记它。
  • @Boris 两年前我回答了姚哥想要的。现在 cosmic_inquiry 添加了赏金以向该答案添加更多细节,而不是该行为的解决方案;)

标签: python decorator


【解决方案1】:

这是因为函数调用中没有提供kwargs。而装饰者logger 对此一无所知,也不知道将使用什么功能。它是那里提供的kwargs 和真实通话之间的一种“代理”。

请参见以下示例:

# kwargs are not provided (not redefined), function `foo1` will use default.
>>> foo1(6, 7, 8)
(6, 7, 8)
{}
2

# new kwargs are provided and passed to decorator too
>>> foo1(6, 7, 8, x=9, y=10)
(6, 7, 8)
{'x': 9, 'y': 10}
90

这类似于:

def foo1(a, b, c, x=2, y=1):
    print(x * y)


def logger(func):
    def inner(*args, **kwargs):
        print(args)
        print(kwargs)
        return func(*args, **kwargs)
    return inner


wrapped_foo1 = logger(foo1)
wrapped_foo1(6,7,8)

甚至可以简化为以下,当您可以清楚地看到问题时:

def foo1_decorated(*args, **kwargs):
    print(args)  # <-- here it has no chance to know that `x=2, y=1`
    print(kwargs)
    return foo1(*args, **kwargs)

foo1_decorated(6, 7, 8)

【讨论】:

  • @cosmic-inquiry,添加了更多示例。最后一个应该表明它们只是不同的 kwargs。装饰器(记录器)像代理一样工作。
  • 你解释了问题,但如果你解释如何实现姚哥想要的也很好,你可以从我链接的问题中复制/粘贴。
  • @Boris yao Ge 只想解释“为什么?”不工作:)
【解决方案2】:

问题是参数的默认值在你调用它时被包装函数对象填充,因为只有被包装函数知道它们(它们存储在__defaults____kwdefaults__中)。 如果你想让你的装饰器也知道它们,你必须模仿被包装的函数对象会做什么。 对于此任务,您可以使用inspect 模块:

from inspect import signature

def logger(func):
    sig = signature(func)
    def inner(*args, **kwargs):
        arguments = sig.bind(*args, **kwargs)    # these 2 steps are normally handled by func
        arguments.apply_defaults()
        print(func, "was called with", arguments)
        return func(*args, **kwargs)
    return inner

@logger
def foo1(a, b, c, x=2, y=1):
    print(x * y)

foo1(6,7,8)

输出:

<function foo1 at 0x7f5811a18048> was called with <BoundArguments (a=6, b=7, c=8, x=2, y=1)>
2

如果您想访问参数,请在docs 中阅读更多相关信息。

【讨论】:

    【解决方案3】:

    那个字典是空的,因为你没有在 foo1 中传递任何 kwargs。

    要获取 x 和 y 而不是空字典,您可以使用

    foo1(6,7,8, x=2, y=3) # x and y are printed while printing kwargs
    

    而不是

    foo1(6,7,8)  # no x and y values are passed so empty dict is print while printing kwargs
    

    请注意,您应该只使用变量 x 和 y。任何其他变量都会导致错误。

    真正发生的过程是这样的:

    1. foo1 function is tried to called
    2. Due to presence of @logger, logger function is called first
    3. foo1 function is passed to logger function.
    4. inner function takes both type of arguments of foo1 function.
    4. *args accepts arguments that are comma separated and should not
       contain key = value type of argument
    5. **kwargs accepts arguments that are only key = value type
    6. Since you have passed 6,7,8, they are all treated as *args 
    7. To pass as **kwargs, you have to pass key = value in foo1 parameters.
    8. *args and ** kwargs values are printed
    9. foo1 function is called 
    10. It executes code inside foo1
    

    【讨论】:

      猜你喜欢
      • 2018-08-28
      • 1970-01-01
      • 1970-01-01
      • 2021-08-19
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      • 2016-04-22
      相关资源
      最近更新 更多