Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Overloading asynchronous methods

I'm building a wrapper for an API. For now, it works. However, it is synchronous, using the requests module for HTTP calls.

I would like to implement a way to asynchronously call these methods while not having to use a different name or lib version. I immediately thought about overloading, but seeing as overloading in python is a bit different than other languages, it doesn't really look possible.

Essentially, I want to build a class that would look like this (idea-wise, I know that it doesn't work in Python):

class Foo:
    def foo(self):
        # Requests code...
        print("foo sync")

    async def foo(self):
        # aiohttp code...
        print("foo async")

And use it in this way:

f = Foo()
f.foo()
await f.foo()

Output:

>> "foo sync"
>> "foo async"

Essentially, in this code, the async function would just completely override the previous one, which isn't really helpful.

From some googling, it doesn't exactly look possible, however, Python always manages to surprise me.

Thanks in advance :D

like image 360
Plutoberth Avatar asked Sep 22 '18 17:09

Plutoberth


1 Answers

Question: Overloading asynchronous methods

Consider the following class definitions, using Inheritance with Overloading!

You have a Base class Foo: from which inherits class Foo_async(Foo):.
Therefore you can reuse most of your implementation, in sync and async mode.
The class Foo_async mainly implements only the async/await requirements.

Note: I'm using asyncio. For example:

import asyncio

class Foo():
    def __init__(self):
        self.mode = "sync"

    def __repr__(self):
        return "{}::foo() {}".format(self.__class__.__name__, self.mode)

    def foo(self):
        print("{}".format(self.__repr__()))
        print("\t\tworkload {}".format(1))
        print("\t\tworkload {}".format(2))

class Foo_async(Foo):
    def __init__(self):
        self.mode = "async awaited"

    async def foo(self):
        super().foo()

Usage:

async def run(foo):
    await foo.foo()

if __name__ == '__main__':
    Foo().foo()

    loop = asyncio.get_event_loop()
    loop.run_until_complete(run(Foo_async()))
    loop.close()

Output:

Foo::foo() sync
        workload 1
        workload 2
Foo_async::foo() async awaited
        workload 1
        workload 2

If you want more granularity, split the workload in def foo(...) into seperate workload functions.
Now you are able to call this functions using await.

For example: (Show, only changed code!)

class Foo():
    ...

    def foo(self):
        print("{}".format(self.__repr__()))
        self.workload_1()
        self.workload_2()

    def workload_1(self):
        print("\t\t{} workload {}".format(self.mode, 1))

    def workload_2(self):
        print("\t\t{} workload {}".format(self.mode, 2))

class Foo_async(Foo):
    ...       

    async def foo(self):
        print("{}".format(self.__repr__()))
        await self.workload_1()
        await self.workload_2()

    async def workload_1(self):
        super().workload_1()

    async def workload_2(self):
        super().workload_2()

Output:

Foo::foo() sync
        sync workload 1
        sync workload 2
Foo_async::foo() async awaited
        async awaited workload 1
        async awaited workload 2

Tested with Python:3.5.3

like image 149
stovfl Avatar answered Nov 15 '22 16:11

stovfl