import asyncio class a : async def m1(self) : # await... return self async def m2(self) : # await... return self async def m3(self) : # await... print(1) async def s() : A = a() await A.m1().m2().m3() loop = asyncio.get_event_loop() loop.run_until_complete(s()) # ----------------------------- AttributeError: 'coroutine' object has no attribute 'm3' 

Is it possible to somehow implement this?

    2 answers 2

    You can call in the chain so await (await (await A.m1()).m2()).m3() It looks creepy, but it works. You can certainly do it the way you want, but not by standard means, as far as I know.

    UPDATE: I was curious and I threw the option

     import asyncio import random class A: async def m1(self): print('TEST 1 BEGIN') await asyncio.sleep(random.randint(1, 5)) print('TEST 1 END') async def m2(self): print('TEST 2 BEGIN') await asyncio.sleep(random.randint(1, 5)) print('TEST 2 END') async def m3(self): print('TEST 3 BEGIN') await asyncio.sleep(random.randint(1, 5)) print('TEST 3 END') async def breaker(self): for x in range(3): await asyncio.sleep(random.randint(1, 2)) print('KU-KU!') class Chain: def __init__(self, obj): self.obj = obj self.chain = [] def __getattr__(self, name): attr = getattr(self.obj, name) if callable(attr): self.chain.append(attr) else: raise ValueError('Only methods can be chained!') return self def __call__(self, *args, **kwargs): return self async def execute(self): result = None for i in self.chain: if result: result = await i(result) else: result = await i() return result async def main(): a = A() futures = [ asyncio.ensure_future(Chain(a).m1().m2().m3().execute()), asyncio.ensure_future(a.breaker()) ] await asyncio.wait(futures) loop = asyncio.get_event_loop() loop.run_until_complete(main()) 

    I think it can be developed to the point that you need.

    UPDATE 2: Adding a method to the Chain class

     def __await__(self): return self.execute().__await__() 

    it can be called almost as required - await Chain(a).m1().m2().m3()

    • Interesting idea! True, it was necessary to be able to pass arguments to methods, not to take them from the result of calling the previous method, but this is easy to fix. Thank! - user203925

    To execute several asynchronous methods in order one by one (the following method does not start until the previous one is completed):

     o = await o.m1() o = await o.m2() o = await o.m3() 

    As shown by @Sergey Gornostaev , it is easy to define such a Chain class so that you can write:

     await Chain(o).m1().m2().m3() 

    In real code, method names are usually longer and therefore both approaches must be equally readable. The first approach is more explicit (and therefore it may be clearer what is happening).

    Such a fluent interface should be used extremely moderately in Python (the code on Python usually follows the incompatible Command – query separation principle ) and see if there is a better, more idiomatic alternative in each particular case. In cases where such an API is justified, it is desirable to return immutable objects from similar methods, for example, as the Query API in SQLAlchemy does.

    If you already have a list of coroutines, then you can use a regular for cycle to execute them sequentially:

     for coro in [asyncio.sleep(1) for _ in range(3)]: await coro 

    The code is executed for 3 seconds, despite the fact that all coroutines are created immediately (predictably), that is, each call is waiting for the end of the previous one.

    For comparison, here is the code that runs in ~ 1 second (all functions are executed at the same time):

     await asyncio.wait([asyncio.sleep(1) for _ in range(3)]) 

    If there is a list of asynchronous methods, then to execute them sequentially, passing the result of previous calls to subsequent ones, you can again use a normal loop:

     obj = C() for method in [Cm] * 3: obj = await method(obj) 

    In some cases, when all methods with one object work and their meaning is to return the next available result, you can use the async for cycle :

     async for line in reader: print('>>>', line) 

    where reader is an object that implements __aiter__() .