如何在构造函数或类体中定义具有await
的类?
例如我想要的东西:
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'
或类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)
我的解决方案(但我希望看到更优雅的方式)
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()
答案 0 :(得分:71)
大多数魔术方法都无法与async def
/ await
一起使用 - 通常,您应该只在专用异步魔术方法中使用await
- __aiter__
,__anext__
,__aenter__
和__aexit__
。在其他魔法方法中使用它或者根本不起作用(如__init__
的情况),或者强制你总是使用异步上下文中魔术方法调用的任何触发器。
现有的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)
其他库使用创建对象的顶级协同程序函数,而不是工厂方法:
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)
您要在create_pool
中调用的aiopg
__init__
功能实际上正在使用此确切模式。
这至少解决了__init__
问题。我还没有看到可以在野外进行异步调用的类变量,所以我不知道已经出现了任何完善的模式。
答案 1 :(得分:17)
我建议使用单独的工厂方法。它安全而直接。但是,如果您坚持使用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
,请举例说明:
@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())
<class '__main__.Foo'>
True
<强>输出:强>
coroutine
<强>解释强>
您的类构造必须返回 canvas = new CanvasPanel();
canvas.setBackground(Color.white);
canvas.addMouseListener(new PointListener());
canvas.addMouseMotionListener(new PointListener());
JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, canvas);
class CanvasPanel extends JPanel
{
public void paintComponent(Graphics page)
{
super.paintComponent(page);
if (mouseDragged == true)
{
page.drawRect(x1, y1, x3, y3);
canvas.repaint();
}
}
}
class PointListener implements MouseListener, MouseMotionListener
{
public void mousePressed (MouseEvent event)
{
mouseDragged = true;
x1 = event.getX();
y1 = event.getY();
}
public void mouseReleased (MouseEvent event)
{
// some code
}
public void mouseDragged(MouseEvent event)
{
x3 = event.getX();
y3 = event.getY();
canvas.repaint();
}
对象而不是其自己的实例。
答案 2 :(得分:17)
另一种方法,用于测试:
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
答案 3 :(得分:5)
如果您使用的是Python3.7或更高版本,则可以使用asyncio.run:
import asyncio
# some code
class Foo(object):
async def __init(self):
self.pool = await create_pool(dsn)
def __init__(self, settings):
self.settings = settings
asyncio.run(self.__init)
foo = Foo(settings)
请注意,如果您要在已经运行的异步函数中实例化Foo
,则此操作将无效。请参见this blog post,以获取有关如何处理这种情况的讨论以及有关Python中异步编程的很好的讨论。
答案 4 :(得分:3)
@ojii的[几乎]规范答案
@dataclass
class Foo:
settings: Settings
pool: Pool
@classmethod
async def create(cls, settings: Settings, dsn):
return cls(settings, await create_pool(dsn))
答案 5 :(得分:1)
更好的是,您可以执行以下操作,这很容易:
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))
基本上,这里发生的事情是像平常一样先调用__init__()
,然后再调用__await__()
,然后等待async_init()
。
答案 6 :(得分:1)
我想展示一种在 __init__
方法中启动基于协程的方法的更简单的方法。
import asyncio
class Foo(object):
def __init__(self, settings):
self.settings = settings
loop = asyncio.get_event_loop()
self.pool = loop.run_until_complete(create_pool(dsn))
foo = Foo(settings)
需要注意的重要一点是:
__init__
将是一个不错的选择。await foo.pool.get(value)
await
调用启动,您将收到 RuntimeError: This event loop is already running
答案 7 :(得分:0)
根据您的需要,您还可以从以下位置使用 AwaitLoader
:
https://pypi.org/project/async-property/
来自文档:
<块引用>AwaitLoader
将在加载属性之前调用 await instance.load()
(如果存在)。
答案 8 :(得分:0)
带有 __ainit__
"async-constructor" 的 AsyncObj 类:
class AsyncObj(metaclass=AsyncObjMcs):
def __init__(self, *args, **kwargs):
"""
Standard constructor used for arguments pass
Do not override. Use __ainit__ instead
"""
self.__storedargs = args, kwargs
self.async_initialized = False
async def __ainit__(self, *args, **kwargs):
""" Async constructor, you should implement this """
async def __initobj(self):
""" Crutch used for __await__ after spawning """
assert not self.async_initialized
self.async_initialized = True
await self.__ainit__(*self.__storedargs[0], **self.__storedargs[1]) # pass the parameters to __ainit__ that passed to __init__
return self
def __await__(self):
return self.__initobj().__await__()
def __init_subclass__(cls, **kwargs):
assert asyncio.iscoroutinefunction(cls.__ainit__) # __ainit__ must be async
@property
def async_state(self):
if not self.async_initialized:
return "[initialization pending]"
return "[initialization done and successful]"
这是“异步类”的示例:
class MyAsyncObject(AsyncObj):
async def __ainit__(self, param1, param2=0):
print("hello!", param1, param2)
# go something async, e.g. go to db
用法:
async def example():
my_obj = await MyAsyncObject("test", 123)