Python __await__ Method

Complete guide to Python's __await__ method covering async programming, coroutines, and awaitable objects.

Python __await__ Method

Python await Method

Last modified April 8, 2025

This comprehensive guide explores Python’s await method, the special method that makes objects awaitable. We’ll cover async programming, coroutines, awaitable objects, and practical examples.

Basic Definitions

The await method returns an iterator that is used to implement awaitable objects. It allows an object to be used with the await expression in async functions.

Key characteristics: it must return an iterator, is used by the async/await syntax, and enables custom awaitable objects. It’s part of Python’s async programming model introduced in Python 3.5.

Basic await Implementation

Here’s a simple implementation showing how await makes a custom object awaitable. The method must return an iterator.

basic_await.py

class SimpleAwaitable: def await(self): yield return “Done”

async def main(): result = await SimpleAwaitable() print(result) # Output: Done

import asyncio asyncio.run(main())

This example creates a basic awaitable object. The await method yields once and returns a value. When awaited, it pauses execution until the yield completes.

The yield is crucial - it makes the method a generator, which is required for await. The returned value becomes the await expression’s result.

Awaitable with Asynchronous Operation

This example shows a more practical awaitable that simulates an async operation with a delay.

async_operation.py

class AsyncOperation: def init(self, delay): self.delay = delay

def __await__(self):
    yield from asyncio.sleep(self.delay)
    return f"Completed after {self.delay}s"

async def main(): op = AsyncOperation(1.5) print(“Starting operation”) result = await op print(result)

asyncio.run(main())

This awaitable object uses asyncio.sleep to simulate an async operation. The yield from delegates to another awaitable.

When awaited, it pauses execution for the specified delay before returning the completion message. This pattern is common for wrapping async operations.

Custom Future Implementation

This example demonstrates implementing a basic Future-like object with await that can be set with a result later.

custom_future.py

class CustomFuture: def init(self): self._result = None self._done = False

def set_result(self, result):
    self._result = result
    self._done = True

def __await__(self):
    while not self._done:
        yield
    return self._result

async def set_future(future, delay, value): await asyncio.sleep(delay) future.set_result(value)

async def main(): future = CustomFuture() asyncio.create_task(set_future(future, 2, “Future result”)) print(“Waiting for future…”) result = await future print(f"Got: {result}")

asyncio.run(main())

This CustomFuture can be awaited until its result is set. The await method yields until _done becomes True, then returns the result.

This mimics how real Futures work in asyncio, allowing decoupled result setting and awaiting. The pattern is useful for bridging callback-based APIs with async.

Chaining Awaitables

This example shows how to chain multiple awaitable objects together using await.

chaining_awaitables.py

class AddAwaitable: def init(self, a, b): self.a = a self.b = b

def __await__(self):
    result = yield from self.a.__await__()
    result += yield from self.b.__await__()
    return result

async def slow_add(a, b): await asyncio.sleep(1) return a + b

async def main(): a = slow_add(10, 20) b = slow_add(30, 40) combined = AddAwaitable(a, b) result = await combined print(f"Total: {result}") # Output: Total: 100

asyncio.run(main())

The AddAwaitable class chains two awaitables, summing their results. It uses yield from to await each operand sequentially.

This demonstrates how await can compose multiple async operations into new awaitable objects. The pattern is useful for building complex async workflows.

Async Context Manager with await

This example implements an async context manager using await for resource management.

async_context.py

class AsyncResource: def init(self, name): self.name = name

async def __aenter__(self):
    print(f"Opening {self.name}")
    await asyncio.sleep(0.5)
    return self

async def __aexit__(self, *args):
    print(f"Closing {self.name}")
    await asyncio.sleep(0.5)

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

async def main(): async with AsyncResource(“DB Connection”) as resource: print(f"Using {resource.name}") await asyncio.sleep(1)

# Alternative using await directly
resource = await AsyncResource("File")
try:
    print(f"Using {resource.name}")
    await asyncio.sleep(1)
finally:
    await resource.__aexit__(None, None, None)

asyncio.run(main())

This shows two ways to use an async resource: with async with and directly with await. The await method delegates to aenter.

The pattern is useful when you need both context manager and direct await support. It demonstrates how await can integrate with other async protocols.

Best Practices

  • Always return an iterator: await must return an iterator

  • Use yield or yield from: Required to make the method a generator

  • Consider cancellation: Handle asyncio.CancelledError appropriately

  • Document await behavior: Clearly document what the await does

  • Prefer async def for coroutines: Only use await for custom awaitables

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.

ad ad