How to set class attribute with await in __init__

匿名 (未验证) 提交于 2019-12-03 01:23:02

问题:

How can I define a class with await in the constructor or class body?

For example what I want:

import asyncio  # some code   class Foo(object):      async def __init__(self, settings):         self.settings = settings         self.pool = await create_pool(dsn)  foo = Foo(settings) # it raises: # TypeError: __init__() should return None, not 'coroutine' 

or example with class body attribute:

class Foo(object):      self.pool = await create_pool(dsn)  # Sure it raises syntax Error      def __init__(self, settings):         self.settings = settings  foo = Foo(settings) 

My solution (But I would like to see a more elegant way)

class Foo(object):      def __init__(self, settings):         self.settings = settings      async def init(self):         self.pool = await create_pool(dsn)  foo = Foo(settings) await foo.init() 

回答1:

Most magic methods aren't designed to work with async def/await - in general, you should only be using await inside the dedicated asynchronous magic methods - __aiter__, __anext__, __aenter__, and __aexit__. Using it inside other magic methods either won't work at all (as is the case with __init__), or will force you to always use whatever triggers the magic method call in an asynchronous context.

Existing asyncio libraries tend to deal with this in one of two ways: First, I've seen the factory pattern used (asyncio-redis, for example):

import asyncio  dsn = "..."  class Foo(object):     @classmethod     async def create(cls, settings):         self = Foo()         self.settings = settings         self.pool = await create_pool(dsn)         return self  async def main(settings):     settings = "..."     foo = await Foo.create(settings) 

Other libraries use a top-level coroutine function that creates the object, rather than a factory method:

import asyncio  dsn = "..."  async def create_foo(settings):     foo = Foo(settings)     await foo._init()     return foo  class Foo(object):     def __init__(self, settings):         self.settings = settings      async def _init(self):         self.pool = await create_pool(dsn)  async def main():     settings = "..."     foo = await create_foo(settings) 

The create_pool function from aiopg that you want to call in __init__ is actually using this exact pattern.

This at least addresses the __init__ issue. I haven't seen class variables that make asynchronous calls in the wild that I can recall, so I don't know that any well-established patterns have emerged.



回答2:

I would recommend a separate factory method. It's safe and straightforward. However, if you insist on a async version of __init__(), here's an example:

def asyncinit(cls):     __new__ = cls.__new__      async def init(obj, *arg, **kwarg):         await obj.__init__(*arg, **kwarg)         return obj      def new(cls, *arg, **kwarg):         obj = __new__(cls, *arg, **kwarg)         coro = init(obj, *arg, **kwarg)         #coro.__init__ = lambda *_1, **_2: None         return coro      cls.__new__ = new     return cls 

Usage:

@asyncinit class Foo(object):     def __new__(cls):         '''Do nothing. Just for test purpose.'''         print(cls)         return super().__new__(cls)      async def __init__(self):         self.initialized = True 

async def f():     print((await Foo()).initialized)  loop = asyncio.get_event_loop() loop.run_until_complete(f()) 

Output:

 True 

Explanation:

Your class construction must return a coroutine object instead of its own instance.



回答3:

Another way to do this, for funsies:

class aobject(object):     """Inheriting this class allows you to define an async __init__.      So you can create objects by doing something like `await MyClass(params)`     """     async def __new__(cls, *a, **kw):         instance = super().__new__(cls)         await instance.__init__(*a, **kw)         return instance      async def __init__(self):         pass  #With non async super classes  class A:     def __init__(self):         self.a = 1  class B(A):     def __init__(self):         self.b = 2         super().__init__()  class C(B, aobject):     async def __init__(self):         super().__init__()         self.c=3  #With async super classes  class D(aobject):     async def __init__(self, a):         self.a = a  class E(D):     async def __init__(self):         self.b = 2         await super().__init__(1)  # Overriding __new__  class F(aobject):     async def __new__(cls):         print(cls)         return await super().__new__(cls)      async def __init__(self):         await asyncio.sleep(1)         self.f = 6  loop = asyncio.get_event_loop() e = loop.run_until_complete(E()) e.b # 2 e.a # 1  c = loop.run_until_complete(C()) c.a # 1 c.b # 2 c.c # 3  f = loop.run_until_complete(F()) # Prints F class f.f # 6 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!