We can define coroutine methods on custom Python objects.
This allows methods on custom Python objects to use async/await syntax, such as awaiting other coroutines and tasks and allows the custom coroutine methods themselves to be awaited within our asyncio programs.
In this tutorial, you will discover how to define object methods as coroutines.
Let’s get started.
What is a Coroutine
A coroutine represents a special type of function that can pause its execution at specific points without blocking other tasks.
It allows for concurrent and non-blocking operations, enabling asynchronous programming.
Coroutines are declared using the “async def” expression in Python, distinguishing them from regular functions.
For example:
1 2 3 |
# define a coroutine async def coro(): # ... |
They can be paused or suspended using the await expression within their body, allowing other coroutines or tasks to run while waiting for potentially blocking operations like I/O or delays.
Coroutines are a fundamental building block in asynchronous programming with Python, enabling efficient handling of concurrent tasks without resorting to thread-based parallelism.
They facilitate cooperative multitasking and make it easier to write asynchronous code that remains responsive and scalable.
You can learn more about coroutines in the tutorial:
Run loops using all CPUs, download your FREE book to learn how.
How to Define Object Methods as Coroutines
We can define object methods as coroutines.
Recall, a method is a function that is defined in a class and has access to the object instance via an argument called self.
method: A function which is defined inside a class body. If called as an attribute of an instance of that class, the method will get the instance object as its first argument (which is usually called self).
— Python Glossary
For example:
1 2 3 4 |
# custom class class MyObject(object): def custom_method(self): pass |
The method can then be called on the object.
For example:
1 2 3 4 5 |
... # create the object obj = MyObject() # call the method obj.custom_method() |
We can define methods that are coroutines.
This means that they return coroutine objects that can be awaited.
For example, we can define a method as a coroutine using the “async def” expression.
As a coroutine, it means the method can then await other coroutines and tasks.
1 2 3 4 5 |
# custom class class MyObject(object): async def custom_method(self): # await another coroutine await asyncio.sleep(1) |
We can then create the object and await the coroutine within our asyncio program.
For example:
1 2 3 4 5 |
... # create the object obj = MyObject() # await the coroutine await obj.custom_method() |
A coroutine method must be awaited within an asyncio program.
If it is called in a regular program, it will result in a RuntimeWarinng as the created coroutine object will not be executed.
You can learn more about this in the tutorial:
Now that we know how to define object methods as coroutines, let’s look at some worked examples.
Example of Object Method As Coroutine
We can explore an example of defining an object method as a coroutine.
In this example, we will define a custom class that has a constructor, a regular method, and a coroutine method. We will then create and use this object within our asyncio program.
Firstly, we can define our custom class.
The constructor of the custom class will take a data argument and store it as an attribute or member variable.
The class will provide a regular method that reports the value of the data attribute with a print statement. It also provides a coroutine method that awaits the sleep coroutine for a moment and then reports the value of the data attribute with a print statement.
The CustomClass below implements this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# define a custom class class CustomClass(object): # constructor def __init__(self, data): # store the data self.data = data # regular method def report_data(self): # report the value of data print(f'Method: {self.data}') # method as coroutine async def sleep_and_report_data(self): # suspend a moment await asyncio.sleep(2) # report the data print(f'Coroutine: {self.data}') |
Next, we can define the main coroutine for our asyncio program.
This will create our CustomClass instance and provide it with a string value that will be stored in the data attribute. We will then call the regular method, then await the coroutine method.
The main() coroutine below implements this.
1 2 3 4 5 6 7 8 |
# main coroutine async def main(): # create the custom object obj = CustomClass('123') # call a method on the custom object obj.report_data() # await coroutine method await obj.sleep_and_report_data() |
Tying this together, the complete example below implements this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# SuperFastPython.com # example of object method as coroutine import asyncio # define a custom class class CustomClass(object): # constructor def __init__(self, data): # store the data self.data = data # regular method def report_data(self): # report the value of data print(f'Method: {self.data}') # method as coroutine async def sleep_and_report_data(self): # suspend a moment await asyncio.sleep(2) # report the data print(f'Coroutine: {self.data}') # main coroutine async def main(): # create the custom object obj = CustomClass('123') # call a method on the custom object obj.report_data() # await coroutine method await obj.sleep_and_report_data() # start the event loop asyncio.run(main()) |
Running the example first creates the main() coroutine and starts the asyncio event loop.
The main() coroutine runs and creates an instance of our CustomClass class and passes in a string as an argument to the constructor.
The class constructor runs and stores the argument in an instance variable.
The main() coroutine then calls the report_data() regular method on the object instance, which reports the value of the instance variable.
Next, the main() coroutine awaits the sleep_and_report_data() coroutine method.
This suspends the main() coroutine and the sleep_and_report_data() runs. It suspends for a moment with a sleep, then resumes and reports the value of the instance variable before terminating.
Finally, the main() coroutine resumes and terminates, closing the event loop.
This highlights how we can define a coroutine method and await it within our asyncio program alongside calling regular object methods.
1 2 |
Method: 123 Coroutine: 123 |
Next, let’s look at the effect of calling a coroutine method outside of an asyncio program.
Free Python Asyncio Course
Download your FREE Asyncio PDF cheat sheet and get BONUS access to my free 7-day crash course on the Asyncio API.
Discover how to use the Python asyncio module including how to define, create, and run new coroutines and how to use non-blocking I/O.
Example Cannot Call Coroutine Methods Outside of Asyncio
We can explore an example of calling a coroutine method like a regular method outside of an asyncio program.
In this case, we can update the above example to create an instance of our CustomClass class and call both methods directly in a regular Python program.
1 2 3 4 5 6 7 |
... # create the custom object obj = CustomClass('123') # call a method on the custom object obj.report_data() # await coroutine method obj.sleep_and_report_data() |
We expect this to raise a RuntimeWarning given that a coroutine was created and not executed.
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# SuperFastPython.com # example of object method as coroutine import asyncio # define a custom class class CustomClass(object): # constructor def __init__(self, data): # store the data self.data = data # regular method def report_data(self): # report the value of data print(f'Method: {self.data}') # method as coroutine async def sleep_and_report_data(self): # suspend a moment await asyncio.sleep(2) # report the data print(f'Coroutine: {self.data}') # create the custom object obj = CustomClass('123') # call a method on the custom object obj.report_data() # await coroutine method obj.sleep_and_report_data() |
Running the example first creates an instance of our CustomClass object and passes it a string argument that is stored in an instance variable of the object.
The report_data() method is then called which then reports the value of the instance variable stored in the object.
Next, the sleep_and_report_data() coroutine method is called. This creates and returns a coroutine object.
The program then terminates. Because the created coroutine object was never executed, a RuntimeWarning is reported.
This highlights that we cannot call a coroutine method like a regular method outside of an asyncio program.
1 2 3 |
Method: 123 RuntimeWarning: coroutine 'CustomClass.sleep_and_report_data' was never awaited RuntimeWarning: Enable tracemalloc to get the object allocation traceback |
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Takeaways
You now know how to define object methods as coroutines.
Did I make a mistake? See a typo?
I’m a simple humble human. Correct me, please!
Do you have any additional tips?
I’d love to hear about them!
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Raimond Klavins on Unsplash
Do you have any questions?