使用 functools.wraps 装饰器尝试类的每个函数

发布于 2025-01-09 13:44:28 字数 903 浏览 5 评论 0原文

我试图定义一个装饰器来执行一个类方法,首先尝试,如果检测到错误,则提出它并提及失败的方法,以便用户可以看到哪个方法出现错误。

这里我展示了我的代码的MRE(最小的、可重现的示例)。

from functools import wraps

def trier(func):
    """Decorator for trying A-class methods"""
    @wraps(func)
    def inner_func(self, name, *args):
        
        try:
            func(self, *args)
        
        except:
            print(f"An error apeared while {name}")
    
    return inner_func
    
class A:
    def __init__(self):
        self._animals = 2
        self._humans = 5
    
    @trier('getting animals')
    def animals(self, num):
        return self._animals + num
    
    @trier('getting humans')
    def humans(self):
        return self._humans

A().animals

出现许多错误,例如:

类型错误:inner_func() 缺少 1 个必需的位置参数:“名称”

或误解 self 类与 self 函数。

I'm trying to define a decorator in order to execute a class method, try it first and, if an error is detected, raise it mentioning the method in which failed, so as to the user could see in which method is the error.

Here I show a MRE (Minimal, Reproducible Example) of my code.

from functools import wraps

def trier(func):
    """Decorator for trying A-class methods"""
    @wraps(func)
    def inner_func(self, name, *args):
        
        try:
            func(self, *args)
        
        except:
            print(f"An error apeared while {name}")
    
    return inner_func
    
class A:
    def __init__(self):
        self._animals = 2
        self._humans = 5
    
    @trier('getting animals')
    def animals(self, num):
        return self._animals + num
    
    @trier('getting humans')
    def humans(self):
        return self._humans

A().animals

Many errors are raising, like:

TypeError: inner_func() missing 1 required positional argument: 'name'

or misunderstanding self class with self function.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

画▽骨i 2025-01-16 13:44:28

作为Stefan答案的替代方案,下面简单地使用@trier而不带任何参数来装饰函数,然后在打印错误消息时我们可以使用func.__name__。

from functools import wraps

def trier(func):
    """Decorator for trying A-class methods"""
    @wraps(func)
    def inner_func(self, *args, **kwargs):

        try:
            return func(self, *args, **kwargs)

        except:
            print(f"An error apeared in {func.__name__}")

    return inner_func

class A:
    def __init__(self):
        self._animals = 2
        self._humans = 5

    @trier
    def animals(self, num):
        return self._animals + num

    @trier
    def humans(self):
        return self._humans

print(A().animals(1))

我还修复了代码中的几个错误:在 trier 的 try 和 except 中,调用 func 的结果从未返回,并且您需要包含 *除了 *args 之外,还有 *kwargs,以便您可以使用命名参数。即 A().animals(num=1) 仅在处理 kwargs 时才有效。

As an alternative to Stefan's answer, the following simply uses @trier without any parameters to decorate functions, and then when printing out the error message we can get the name with func.__name__.

from functools import wraps

def trier(func):
    """Decorator for trying A-class methods"""
    @wraps(func)
    def inner_func(self, *args, **kwargs):

        try:
            return func(self, *args, **kwargs)

        except:
            print(f"An error apeared in {func.__name__}")

    return inner_func

class A:
    def __init__(self):
        self._animals = 2
        self._humans = 5

    @trier
    def animals(self, num):
        return self._animals + num

    @trier
    def humans(self):
        return self._humans

print(A().animals(1))

I also fixed a couple of bugs in the code: In trier's try and except the result of calling func was never returned, and you need to include **kwargs in addition to *args so you can use named parameters. I.e. A().animals(num=1) only works when you handle kwargs.

征﹌骨岁月お 2025-01-16 13:44:28

对于带有参数的装饰器,您需要多一层嵌套:

from functools import wraps


def trier(name):
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except:
                print(f"An error apeared while executing {name!r}")
        return inner
    return wrapper


class A:

    def __init__(self):
        self._animals = 2
        self._humans = 5

    @trier('getting animals')
    def animals(self, num):
        return self._animals + num

    @trier('getting humans')
    def humans(self):
        return self._hoomans # wrong attribute name


a = A()
a.humans() # An error apeared while executing 'getting humans'

For decorators with parameters, you need one more level of nesting:

from functools import wraps


def trier(name):
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except:
                print(f"An error apeared while executing {name!r}")
        return inner
    return wrapper


class A:

    def __init__(self):
        self._animals = 2
        self._humans = 5

    @trier('getting animals')
    def animals(self, num):
        return self._animals + num

    @trier('getting humans')
    def humans(self):
        return self._hoomans # wrong attribute name


a = A()
a.humans() # An error apeared while executing 'getting humans'
梦回梦里 2025-01-16 13:44:28

我会这样做,希望它有帮助。

from functools import wraps
import sys


def trier(func):
    """Decorator for trying A-class methods"""
    @wraps(func)
    def inner_func(self, *args, **kwargs):
        print(f'Calling {func.__name__}')
        try:
            func(self, *args, **kwargs)

        except:
            print(f"An error apeared on function : {func.__name__}")
            e = sys.exc_info()[2]
            raise Exception(f"Exception occured on line: {e.tb_next.tb_lineno}")

    return inner_func


class A:
    def __init__(self):
        self._animals = 2
        self._humans = 5

    @trier
    def get_animals(self, num):
        return self._animals + num

    @trier
    def get_humans(self):
        return self._humans

    @trier
    def function_raising_exception(self):
        raise Exception('This is some exception')


if __name__ == "__main__":
    a = A()
    a.get_animals(2)
    a.function_raising_exception()

使用e = sys.exc_info()[2],您还可以获得回溯消息并指向发生异常的行。

值得注意的是,当编写装饰器时,它应该适用于您在应用程序中使用的其他功能。

I would do this like this, hope it helps.

from functools import wraps
import sys


def trier(func):
    """Decorator for trying A-class methods"""
    @wraps(func)
    def inner_func(self, *args, **kwargs):
        print(f'Calling {func.__name__}')
        try:
            func(self, *args, **kwargs)

        except:
            print(f"An error apeared on function : {func.__name__}")
            e = sys.exc_info()[2]
            raise Exception(f"Exception occured on line: {e.tb_next.tb_lineno}")

    return inner_func


class A:
    def __init__(self):
        self._animals = 2
        self._humans = 5

    @trier
    def get_animals(self, num):
        return self._animals + num

    @trier
    def get_humans(self):
        return self._humans

    @trier
    def function_raising_exception(self):
        raise Exception('This is some exception')


if __name__ == "__main__":
    a = A()
    a.get_animals(2)
    a.function_raising_exception()

Using e = sys.exc_info()[2], you can get the traceback message as well and point to the line at which the exception is occurred.

It's worth noting that when a decorator is written, it should be applicable to other functions that you'd use in your app.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文