繁体   English   中英

如何编写更新关键字参数的 python 装饰器?

[英]How to write python decorator that updates keyword argument?

目标是编写一个装饰器来更新包装的 function 的一个关键字参数。 在以下代码包装器中尝试更新kwarg1

import inspect                                                                                                                                                                                         [0/300678]
from functools import wraps

def override_me(arg, kwarg1="default kwarg1", kwarg2="default kwarg2"):
    print(f"override_me {arg} kwarg1={kwarg1} kwarg2={kwarg2}")

def append_kwarg1(func):
    original_kwarg1_default = (
        inspect.signature(func).parameters["kwarg1"].default
    )

    @wraps(func)
    def wrapper(*args, kwarg1=original_kwarg1_default, **kwargs):
        func(*args, kwarg1=kwarg1 + "_patched!", **kwargs)

    return wrapper

override_me = append_kwarg1(override_me)


override_me("passed_arg")
override_me("passed_arg", kwarg1="passed_kwarg1_named")
override_me("passed_arg", "passed_kwarg1_as_arg") # TypeError: override_me() got multiple values for argument 'kwarg1'

但是,当kwarg1作为位置参数传递时,这会失败。

编辑:注释中指出的澄清:无法更改override_me签名(认为:外部模块)。

一个可行的解决方案,基于即使是关键字参数也有索引这一事实。 然后根据调用者传递的args的长度,我们可以确定感兴趣的参数是按位置传递还是通过关键字传递,并在argskwargs中更新它:


import inspect                                                                                                                                                                                         
from functools import wraps

def override_me(arg, kwarg1="default kwarg1", kwarg2="default kwarg2"):
    print(f"override_me {arg} kwarg1={kwarg1} kwarg2={kwarg2}")

def append_kwarg1(func):
    params = inspect.signature(func).parameters
    kwarg1_index = next(
        x[0] for x in zip(range(len(params)), params.items()) if x[1][0] == "kwarg1"
    )

    def update(v):
        return v + "_patched!"

    @wraps(func)
    def wrapper(*args, **kwargs):
        if len(args) > kwarg1_index:
            args = (
                args[:kwarg1_index]
                + (update(args[kwarg1_index]),)
                + args[kwarg1_index + 1 :]
            )
            func(*args, **kwargs)
        else:
            kwargs["kwarg1"] = update(kwargs.get("kwarg1", params["kwarg1"].default))
            func(*args, **kwargs)

    return wrapper


override_me = append_kwarg1(override_me)

override_me("passed_arg", kwarg1="passed_kwarg1_named")
override_me("passed_arg")
override_me("passed_arg", "passed_kwarg1_as_arg")
override_me("passed_arg", "passed_kwarg1_as_arg", "passed_kwarg2_as_arg")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM