在Python装饰器中,'kwargs'为空

7
我下面展示了一个装饰器的演示。

以下是代码:

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}

这个问题在这里有答案:https://dev59.com/31wZ5IYBdhLWcg3wM97Z 但是由于悬赏无法标记。 - user3064538
1
@Boris 我回答了两年前yao Ge想要的问题。现在cosmic_inquiry添加了赏金,以添加更多关于那个答案的细节,而不是解决那个行为的方案 ;) - wowkin2
3个回答

5
那是因为在函数调用中没有提供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。而装饰器(logger)的作用就像代理一样。 - wowkin2
你已经解释了问题,但如果你能解释一下如何实现 Yao Ge 想要的,那就更好了,你可以直接从我提供的链接中复制/粘贴。 - user3064538
@Boris要求的只是解释"为什么?"而不是绕过解决方案 :) - wowkin2

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

如果你想访问参数,请在文档中了解更多信息。


0

那个字典是空的,因为你在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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接