在Python中访问装饰器参数的默认值

hen*_*esu 0 python python-decorators

我正在尝试编写一个自定义 Python 装饰器,它将装饰函数包装在一个try ... except块中,并添加一条带有附加上下文的消息以使调试更容易。

基于不同的资源(例如,参见此处此处),我到目前为止构建了以下内容:

def _exception_handler(msg):
    """Custom decorator to return a more informative error message"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            try:
                # this is the actual decorated function
                func(*args, **kwargs)
            except Exception as e:
                # we catch the exception and raise a new one with the custom
                # message and the original exception as cause
                raise Exception(f"{msg}: {e}") from e
        return wrapper
    return decorator
Run Code Online (Sandbox Code Playgroud)

这按预期工作 - 如果我运行:

@_exception_handler("Foo")
def test():
    raise ValueError("Bar")

test()
Run Code Online (Sandbox Code Playgroud)

这将返回:

Exception: Foo: Bar
Run Code Online (Sandbox Code Playgroud)

现在,我并不总是想通过自定义,msg因为这有时有点多余。所以我设置了一个默认值msg="",我想检查是否msg==""是这种情况,我只想msg根据函数名称重新创建装饰器内部,例如msg = f"An error occurred in {func.__name__}".

然后我想在没有任何msg参数的情况下使用装饰器。我不关心空括号,使用@_exception_handler()对我来说完全没问题。

但不知怎的,这似乎不起作用:

Exception: Foo: Bar
Run Code Online (Sandbox Code Playgroud)

如果我运行:

def _exception_handler(msg=""):
    """Custom decorator to return a more informative error message"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            try:
                # this is the actual decorated function
                func(*args, **kwargs)
            except Exception as e:
                if msg=="":
                    # if no custom message is passed, we just use the function name
                    msg = f"An error occurred in {func.__name__}"
                # we catch the exception and raise a new one with the message
                # and the original exception as cause
                raise Exception(f"{msg}: {e}") from e
        return wrapper
    return decorator
Run Code Online (Sandbox Code Playgroud)

我明白了

UnboundLocalError: local variable 'msg' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

如果我把它放在global message该线的正下方def decorator(func):,我会得到同样的错误。如果我把它放在下面def wrapper(*args, **kwargs):,我会得到:

NameError: name 'msg' is not defined
Run Code Online (Sandbox Code Playgroud)

我有什么想法可以让它发挥作用吗?如果可能的话,我想避免使用任何第三方模块,例如wrapt. 使用标准库中的wrapsfromfunctools当然很好(尽管到目前为止我也没有任何运气)。

小智 5

添加此代码nonlocal msg

def _exception_handler(msg=""):
    """Custom decorator to return a more informative error message"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            nonlocal msg
            try:
                # this is the actual decorated function
                func(*args, **kwargs)
            except Exception as e:
                if msg=="":
                    # if no custom message is passed, we just use the function name
                    msg = f"An error occurred in {func.__name__}"
                # we catch the exception and raise a new one with the message
                # and the original exception as cause
                raise Exception(f"{msg}: {e}") from e
        return wrapper
    return decorator
Run Code Online (Sandbox Code Playgroud)