3

How can I loop over a list of objects async and call their function. For example:

class Cat:
    def talk():
        print("Meow")

class Dog:
    def talk():
        print("Woof")


cat = Cat()
dog = Dog()

animal_list = [cat, dog]

# How would I do these async?
for animal in animal_list:
    animal.talk()

This thread, How to use an async for loop to iterate over a list?, recommends using asyncio, but doesn't example how I can have an object call it's own function such as animal.talk()

3
  • Are you ok with making talk function an async function? It's easier that way. Commented Mar 3, 2020 at 0:38
  • Yes, how would that look? Commented Mar 3, 2020 at 4:06
  • Can you describe in more detail what you would like to achieve? Specifically, what's wrong with the current code, and how you'd like the new code to behave. Note that the linked question doesn't just "recommend" the use of asyncio based on the use case, it is about async for to begin with. Unless you understand what async for does (hint: it doesn't automatically parallelize your loop, one could almost say that it does the opposite - see here or here), it doesn't make sense to use it. Commented Mar 3, 2020 at 10:01

2 Answers 2

1

Make the talk functions async otherwise there's no point using asyncio.

class Cat:
    async def talk():
        print("Meow")

class Dog:
    async def talk():
        print("Woof")


cat = Cat()
dog = Dog()

animal_list = [cat, dog]

Create a list (Iterable) of coroutines returned by animal.talk().

Either coroutines = map(lambda animal : animal.talk(), animal_list) or coroutines = [animal.talk() for animal in animal_list] will do.

Then finally scheduled the list of coroutines for execution.

# This returns the results of the async functions together.
results = await asyncio.gather(coroutines)

# This returns the results one by one.
for future in asyncio.as_completed(coroutines):
    result = await future

cat.talk() and dog.talk() will be executed asynchronously, which means the order of their execution is not guaranteed, and may be run on different threads. But here the talk function is so simple that it will look like it's run synchronously, and confers no real benefits.

But if talk involved making a network request or a long, heavy computation, and the animal_list were very long, doing it this way can help with the performance.

Sign up to request clarification or add additional context in comments.

1 Comment

Seems like a very good answer, but the code does not work for me.
1

I like @Hurried-Helpful's post on this. Making their example work might look as follows.

import asyncio
import random
import time

class Cat:
    @staticmethod
    async def talk():
        await asyncio.sleep(random.choice([1,2]))
        print("Meow")

class Dog:
    @staticmethod
    async def talk():
        await asyncio.sleep(random.choice([0,1]))
        print("Woof")

async def main():
    print(f"Started at: {time.strftime('%X')}")
    cat = Cat()
    dog = Dog()
    animal_list = [cat, dog]
    res = await asyncio.gather(
        *[animal.talk() for animal in animal_list]
    )
    print(f"Ended at: {time.strftime('%X')}")

asyncio.run(main())

The Python docs are much better on async functions now, and also contain good examples: https://docs.python.org/3/library/asyncio-task.html#running-tasks-concurrently

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.