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

2024-06-18 13:11:38 发布

您现在位置:Python中文网/ 问答频道 /正文

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

Tags: selfasyncsettingsobjectfooinitdefcreate
3条回答

我推荐一种单独的工厂方法。这是安全和直接的。但是,如果坚持使用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对象,而不是它自己的实例。

另一种方式,为了搞笑:

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

大多数magic方法不是设计用来处理async def/await-通常,您应该只在专用异步magic方法中使用__aiter____anext____aenter____aexit__。在其他magic方法中使用它可能根本不起作用(就像__init__那样),或者将强制您始终在异步上下文中使用magic方法调用的任何触发器。

现有的asyncio库倾向于用以下两种方法之一来处理这个问题:首先,我看到了使用的工厂模式(例如^{}):

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)

要在__init__中调用aiopg中的create_pool函数实际上使用的正是这个模式。

这至少解决了__init__问题。我还没有见过可以在野外进行异步调用的类变量,所以我不知道是否出现了任何已经建立良好的模式。

相关问题 更多 >