【问题标题】:Decorators in python: Difference in the args interpretation in class and function based decoratorspython中的装饰器:基于类和函数的装饰器中args解释的差异
【发布时间】:2023-03-17 03:34:01
【问题描述】:

我正在尝试在 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

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

【问题讨论】:

  • 我假设您的lifecycle_handler 不是LifeCycleHandler 的方法,对吧?是不是笔误?
  • @Ke Zhang:谢谢您的回复,是的,是错字。我已经更新了问题

标签: python-3.x python-decorators


【解决方案1】:

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

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

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

回到您的问题,从追溯开始,您假设 python 将帮助您将调用者实例(Test 的实例)作为第二个参数传递给__call__。这不是真的。但是,__get__ 中确实如此。

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

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

例如,你有下面的测试代码:

t = Test(123)
t.test_data()

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-28
    • 2011-09-29
    • 2011-06-06
    • 2020-07-15
    • 2018-10-03
    • 2013-09-25
    • 2016-04-20
    • 2012-03-14
    相关资源
    最近更新 更多