python中的装饰器:基于类和函数的装饰器中args解释的差异



我试图在python中创建一个通用的函数生命周期处理程序。工作简介:

  • 记录签名和返回值。
  • 处理异常,是否重试。
  • 在异常情况下提供清理

我在做清理时遇到的问题如下:基于函数的修饰符:

def handler(exception=Exception,cleanup=None):
def func_wrapper(func):
def wrapper(*args,**kwargs):
response=None
try:
print(args)
response=func(*args,**kwargs)
except exception as ex:
print(f'Exception occurred:{str(ex)}nCleaning up resources')
#Passing the object for cleanup, it fails for class based decorators as it does not passes self as argument
cleanup(args[0])
return response
return wrapper
return func_wrapper

应该被清理的数据存储在类实例中,并根据提供的方法进行清理。例如:

  • 使用第三方API存储一些信息

  • 在异常情况下,传递的清理操作将调用删除API。

    class Test:
    def __init__(self,data):
    self.main_data=data
    
    @handler(exception=Exception,cleanup=lambda x:print(f"Cleaning data:{x.main_data}"))
    def test_data(self):
    print(f'Data is :{self.main_data}')
    i=1/0
    
  • 输出:

Exception occurred:division by zero
Cleaning up resources
Cleaning:John Doe

我更倾向于基于类的装饰器。

class LifeCycleHandler:
def __init__(self,*,func=None,exception=Exception,cleanup=None):
self.__exception=exception
self.__cleanup=cleanup
self.__func=func
def __call__(self,*args,**kwargs):
response=None
try:
print(args)
response=self.__func(*args,**kwargs)
except self.__exception as ex:
print(f'Exception occurred:{str(ex)}n cleaning up resources')
#Passing the object for cleanup
self.__cleanup(args[0])
return response
def lifecycle_handler(exception=Exception,cleanup=None):
def wrapper(func): 
response=LifeCycleHandler(func=func,exception=exception,cleanup=cleanup)
return response
return wrapper

使用具有类似功能的基于类的装饰器时,我遇到了以下错误:

()
Exception occurred:test_data() missing 1 required positional argument: 'self'
cleaning up resources
Traceback (most recent call last):
File "test.py", line 27, in __call__
response=self.__func(*args,**kwargs)
TypeError: test_data() missing 1 required positional argument: 'self'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test.py", line 54, in <module>
test.test_data()
File "test.py", line 31, in __call__
self.__cleanup(args[0])
IndexError: tuple index out of range

有人能指导我关于可调用类的参数解释吗?

如果我的评论正确,您可以将__get__添加到LifeCycleHandler

def __get__(self, obj, type=None):
return functools.partial(self.__call__, obj)

这将使test_data成为一个非数据描述符。我想你已经知道了描述符。如果没有,那绝对值得一试。

回到你的问题,从跟踪回来,你假设python将帮助你传递调用者实例,这是Test的实例作为__call__的第二个参数。这不是真的。然而,这在__get__中是正确的。

你的核心逻辑(try/except块)需求是:

  • Test的实例,因为您需要访问main_data
  • LifeCycleHandlerin的实例,因为您需要访问您的self.__func
  • 参数在__get__中不被接受,但你可以在__call__中使用它们。

例如,您的测试代码如下:

t = Test(123)
t.test_data()

t.test_data将调用__get__。在它的参数中,selfLifeCycleHandler的实例,objt(Test的实例)。__get__返回一个可调用函数(__call__),其第一个参数部分由obj提供。

最新更新