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

huangapple go评论73阅读模式
英文:

Accessing default value of decorator argument in Python

问题

Here's the translated code without the code comments:

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

基于不同的资源例如[这里](https://www.geeksforgeeks.org/decorators-with-parameters-in-python/)和[这里](https://www.freecodecamp.org/news/python-decorators-explained-with-examples/)),我到目前为止构建了以下内容

```python
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

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

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

test()

这会返回:

Exception: Foo: Bar

现在,我并不总是想传递自定义的msg,因为有时这有点多余。因此,我将msg=""设置为默认值,并且我想要检查msg=="",如果是这种情况,我只想在装饰器内重新创建msg,类似于msg = f"An error occurred in {func.__name__}"

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

但是某种方式似乎不起作用:

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

如果我运行:

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

test()

我会得到:

UnboundLocalError: local variable 'msg' referenced before assignment

如果我将global message放在def decorator(func):行的下面,我会得到相同的错误。如果我将其放在def wrapper(*args, **kwargs):的下面,我会得到:

NameError: name 'msg' is not defined

有没有办法让这个工作?如果可能的话,我想避免使用wrapt等第三方模块。当然,使用标准库中的functoolswraps也是可以的(尽管到目前为止我还没有成功)。

英文:

I am trying to write a custom Python decorator which wraps the decorated function in a try ... except block and adds a message with additional context to make debugging easier.

Based on different resources (see here and here for example) I so far built the following:

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

This works as expected - if I run:

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

test()

This returns:

Exception: Foo: Bar

Now, I do not always want to pass a custom msg because that's sometimes a bit redundant. So I set a default value of msg="" and I want to check if msg=="" and if that is the case I would just like to re-create the msg inside the decorator based on the function name, something like msg = f"An error occurred in {func.__name__}".

I would then like to use the decorator without any msg argument. I do not care about the empty parantheses, using @_exception_handler() is perfectly fine for me.

But somehow this does not seem to work:

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

If I run:

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

test()

I get

UnboundLocalError: local variable 'msg' referenced before assignment

If I put global message right below the def decorator(func): line, I get the same errors. If I put it below the def wrapper(*args, **kwargs):, I instead get:

NameError: name 'msg' is not defined

Any ideas how I can get this to work? If possible, I would like to avoid any third-party modules such as wrapt. Using wraps from functools from the standard library is fine of course (although I did not have any luck with that so far either).

答案1

得分: 5

添加这段代码 nonlocal msg

def _exception_handler(msg=""):
    """自定义装饰器以返回更详细的错误消息"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            nonlocal msg
            try:
                # 这是实际被装饰的函数
                func(*args, **kwargs)
            except Exception as e:
                if msg == "":
                    # 如果没有传递自定义消息,我们只使用函数名称
                    msg = f"在 {func.__name__} 中发生了错误"
                # 我们捕获异常并引发一个带有消息和原始异常作为原因的新异常
                raise Exception(f"{msg}: {e}") from e
        return wrapper
    return decorator
英文:

add this code 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

huangapple
  • 本文由 发表于 2023年5月25日 23:35:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76334025.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定