Python, asyncio: decorator in class to simplify loop syntax

时间:2017-08-04 12:38:26

标签: python python-asyncio python-decorators

Lets consider the following example of a class containing an asyncio loop and an async coroutine:

import asyncio

class Async:
    def __init__(self):
        self.loop=asyncio.get_event_loop()

    async def function(self, word):
        print(word)
        await asyncio.sleep(1.0)

a=Async()
a.loop.run_until_complete(a.function("hello_world"))

This does work.
I would like to create a decorator so that i can simplify the syntax of the code calling function to

a.function("hello_world")

I tried the following:

class Async:
    def __init__(self):
        self.loop=asyncio.get_event_loop()

    def async_loop(f):
        def decorated(*args, **kwargs):
            self.loop.run_until_complete(f(*args, **kwargs))

    @async_loop
    async def function(self, word):
        print(word)
        await asyncio.sleep(1.0)

a=Async()
a.function("hello_world")

At that point i receive the error: 'NoneType' object is not callable. - I also tried to have the decorator function outside of the class, but i got the same error. I'm not sure whether the decorator function best stands inside the claass (as a method) or outside. I'm quite new to python so Asyncio, decorator, and decorators in classes are still quite confusing for me. Any good soul would have an idea how to do that code correctly?

1 个答案:

答案 0 :(得分:1)

Decorators inside classes are a mess because self has to creep in everywhere.

Here is a working version of your code:

import asyncio

class Async:
    def __init__(self):
        self.loop=asyncio.get_event_loop()

    def async_loop(f):
        def decorated(self, *args, **kwargs):
            self.loop.run_until_complete(f(self, *args, **kwargs))
        return decorated

    @async_loop
    async def function(self, word):
        print(word)
        await asyncio.sleep(1.0)

a=Async()
a.function("hello_world")

You can make it more "selfless" if you just declare the event loop inside async_loop, or even better, declare the decorator outside of the class:

def async_loop(f):
    loop = asyncio.get_event_loop()
    def decorated(*args, **kwargs):
        loop.run_until_complete(f(*args, **kwargs))
    return decorated

class Async:
    @async_loop
    async def function(self, word):
        print(word)
        await asyncio.sleep(1.0)

a=Async()
a.function("hello_world")

So now it starts to raise the question, "why is this in a class in the first place?" And another question, "isn't there a decorator out there that does this already?"