Asyncio:当我们需要使用异步方法时,如何在 __del__ 方法中清理 class 的实例

Asyncio: How to cleanup instance of class in __del__ method when we need to use async methods

假设一个 class 需要一个异步协程来清理:

import asyncio                                                                                                          

class AsyncClient:                                                                                                      

    async def do_something(self):                                                                                       
        print ('Doing something')                                                                                       
        await asyncio.sleep(1)                                                                                          
        print ('Something done')                                                                                        

    async def cleanup(self):                                                                                            
        print ('Starting cleanup')                                                                                      
        await asyncio.sleep(1)                                                                                          
        print ('Cleanup in progress 1/3')                                                                               
        await asyncio.sleep(1)                                                                                          
        print ('Cleanup in progress 2/3')                                                                               
        await asyncio.sleep(1)                                                                                          
        print ('Cleanup in progress 3/3')                                                                               

    def __del__(self):
        **CODE_HERE_SHOULD_CALL_CLEANUP**

不允许在 dunder 方法中使用 await。

我应该在 __del__ 方法中放入什么以允许在这两种情况下进行清理:

client = AsyncClient() 

async def main():                                                                                                                                                                                                        
    await client.do_something()                                                                                         

asyncio.run(main())

async def main():                                                                                                       
    client = AsyncClient()                                                                                              
    await client.do_something()                                                                                         

asyncio.run(main())

我尝试执行以下操作,它适用于第一种情况但不适用于第二种情况(如果循环不再存在,我将重新创建一个循环):

    def __del__(self):                                                                                                  
        print ('__del__()')                                                                                             
        try:                                                                                                            
            loop = asyncio.get_event_loop()                                                                             
        except RuntimeError:                                                                                            
            loop = asyncio.new_event_loop()                                                                             
            asyncio.set_event_loop(loop)                                                                                
            asyncio.run(self.cleanup())                                                                                 
            return                                                                                                      

        cleanup_task = loop.create_task(self.cleanup())

清理不应取决于何时(或是否)调用 __del__。定义一个清理方法,然后显式调用它或让上下文管理器为您隐式调用它。

import asyncio

class AsyncClient:

    async def do_something(self):
        print ('Doing something')
        await asyncio.sleep(1)
        print ('Something done')

    async def cleanup(self):
        print ('Starting cleanup')
        await asyncio.sleep(1)
        print ('Cleanup in progress 1/3')
        await asyncio.sleep(1)
        print ('Cleanup in progress 2/3')
        await asyncio.sleep(1)
        print ('Cleanup in progress 3/3')

    async def __aenter__(self):
       return self

    async def __aexit__(self, *args):
        await self.cleanup()

然后

client = AsyncClient()

async def main():
    await client.do_something()
    await client.cleanup()

asyncio.run(main())

或类似

client = AsyncClient()

async def main():
    async with client:
        await client.do_something()

asyncio.run(main())