Python 如何在 __init__ 中使用 await 设置类属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33128325/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to set class attribute with await in __init__
提问by uralbash
How can I define a class with await
in the constructor or class body?
如何await
在构造函数或类主体中定义类?
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 body 属性的示例:
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()
采纳答案by dano
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.
最神奇的方法不旨在与工作async def
/ await
-在一般情况下,你应该只使用await
专用异步魔术方法里面- ,__aiter__
,__anext__
,__aenter__
和__aexit__
。在其他魔术方法中使用它要么根本不起作用(就像 的情况__init__
),要么会迫使您始终使用在异步上下文中触发魔术方法调用的任何内容。
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):
现有的asyncio
库倾向于通过以下两种方式之一来处理这个问题:首先,我已经看到了使用的工厂模式(asyncio-redis
例如):
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.
您要调用的create_pool
函数实际上是使用这种精确模式。aiopg
__init__
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.
这至少解决了这个__init__
问题。我还没有看到我记得的在野外进行异步调用的类变量,所以我不知道已经出现了任何完善的模式。
回答by Huazuo Gao
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:
我会推荐一个单独的工厂方法。这是安全和直接的。但是,如果您坚持使用 的async
版本__init__()
,请看以下示例:
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:
输出:
<class '__main__.Foo'>
True
Explanation:
解释:
Your class construction must return a coroutine
object instead of its own instance.
您的类构造必须返回一个coroutine
对象而不是它自己的实例。
回答by khazhyk
Another way to do this, for funsies:
另一种方法来做到这一点,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
async def main():
e = await E()
print(e.b) # 2
print(e.a) # 1
c = await C()
print(c.a) # 1
print(c.b) # 2
print(c.c) # 3
f = await F() # Prints F class
print(f.f) # 6
import asyncio
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
回答by Vishnu shettigar
Better yet you can do something like this, which is very easy:
更好的是你可以做这样的事情,这很容易:
import asyncio
class Foo:
def __init__(self, settings):
self.settings = settings
async def async_init(self):
await create_pool(dsn)
def __await__(self):
return self.async_init().__await__()
loop = asyncio.get_event_loop()
foo = loop.run_until_complete(Foo(settings))
Basically what happens here is __init__()
gets called first as usual. Then __await__()
gets called which then awaits async_init()
.
基本上这里发生的事情__init__()
像往常一样首先被调用。然后__await__()
被调用,然后等待async_init()
。
回答by Dima Tisnek
[Almost] canonical answer by @ojii
[几乎]@ojii 的规范答案
@dataclass
class Foo:
settings: Settings
pool: Pool
@classmethod
async def create(cls, settings: Settings, dsn):
return cls(settings, await create_pool(dsn))