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

Try each function of a class with functools.wraps decorator

我正在尝试定义一个 装饰器 以执行 class 方法,先尝试一下 并且,如果检测到错误,则提出它并提及失败的方法,以便用户可以看到错误是在哪个方法中。

这里我展示了我的代码的 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

许多错误正在引发,例如:

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

或误会自我class与自我功能

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

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'

作为 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 的结果从未返回,您需要包含 **kwargs除了 *args 之外,您还可以使用命名参数。 IE。 A().animals(num=1) 仅在处理 kwargs.

时有效

我会这样做,希望对您有所帮助。

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],同样可以得到回溯信息,指向异常发生的行

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