Python装饰器中wrapper的*args与**kwargs参数工作机制咨询
旧版本(仅支持单个参数)
# This version only accepts one argument def shout(fn): def wrapper(name): return fn(name).upper() return wrapper
新版本(支持任意参数)
# This version works with any number of args def shout(fn): def wrapper(*args, **kwargs): print(f"abc is {fn.__name__}") return fn(*args, **kwargs).upper() return wrapper @shout def greet(name): return f"Hi, I'm {name}." @shout def order(main, side): return f"Hi, I'd like the {main}, with a side of {side}, please." @shout def lol(): return "lol" print(greet("todd")) print(order(side="burger", main="fries")) print(lol())
当wrapper函数执行时,它如何获知要赋值给*args和**kwargs的参数值?我们并未在此定义这些参数值,而是为被装饰函数传递了side="burger"、main="fries"这类参数。**kwargs会展开字典,但该字典是何时定义的?side="burger"、main="fries"如何成为wrapper函数的参数并被分配给args和kwargs?为何传给被装饰函数的参数会被分配给wrapper函数的参数?
这问题问得特别关键!核心要搞懂Python装饰器的调用逻辑和参数传递机制,咱们一步步拆解:
装饰器本质是「函数替换」
当你用@shout装饰greet、order这些函数时,Python在后台悄悄执行了这样的操作:greet = shout(greet)。也就是说,原来的greet函数被替换成了shout返回的wrapper函数!所以当你调用greet("todd")时,你其实是在调用wrapper函数,而非最初定义的那个greet。参数直接传给了wrapper
既然实际调用的是wrapper,那你传入的所有参数(不管是位置参数还是关键字参数)自然会直接传递给wrapper。这里的*args和**kwargs是Python的语法糖:*args会把所有位置参数打包成一个元组;**kwargs会把所有关键字参数打包成一个字典。
比如你调用order(side="burger", main="fries"),实际上是调用wrapper(side="burger", main="fries"),这时候wrapper的**kwargs就会自动被赋值为{"side": "burger", "main": "fries"}——这个字典是Python解释器在你调用函数时自动临时创建的,不需要你手动定义。
参数最终会传递给原函数
wrapper里的fn(*args, **kwargs)是「解包」操作:把args元组里的元素作为位置参数传给原函数fn,把kwargs字典里的键值对作为关键字参数传给fn。这样原函数(比如order)就能拿到它需要的main和side参数,正常执行逻辑。整个流程梳理
调用被装饰后的函数 → 实际调用wrapper函数 → wrapper接住所有传入的参数 → wrapper把参数原封不动传给原函数 → 拿到原函数的返回值后做额外处理(比如转大写) → 返回最终结果。
内容的提问来源于stack exchange,提问作者v7676

