如何在__init__中使用await设置class属性

时间:2015-10-14 14:36:09

标签: python python-3.x python-asyncio

如何在构造函数或类体中定义具有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()

9 个答案:

答案 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 从对象运行异步方法。即await foo.pool.get(value)
  • 不要尝试通过 await 调用启动,您将收到 RuntimeError: This event loop is already running

答案 7 :(得分:0)

根据您的需要,您还可以从以下位置使用 AwaitLoaderhttps://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)