How to set class attribute with await in __init__
如何在构造函数或类主体中使用
例如我想要的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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属性的示例:
1 2 3 4 5 6 7 8 | class Foo(object): self.pool = await create_pool(dsn) # Sure it raises syntax Error def __init__(self, settings): self.settings = settings foo = Foo(settings) |
我的解决方案(但我希望看到更优雅的方式)
1 2 3 4 5 6 7 8 9 10 | 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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) |
其他库使用创建对象的顶级协程函数,而不是工厂方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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) |
您要在
这至少解决了
有趣的另一种方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 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()) |
我建议使用单独的工厂方法。安全,简单。但是,如果您坚持使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
用法:
1 2 3 4 5 6 7 8 9 | @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 |
1 2 3 4 5 | async def f(): print((await Foo()).initialized) loop = asyncio.get_event_loop() loop.run_until_complete(f()) |
输出:
1 2 | <class '__main__.Foo'> True |
说明:
您的类构造必须返回
更好的是,您可以执行以下操作,这很容易:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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)) |
基本上,这里发生的是
[几乎] @ojii的规范答案
1 2 3 4 5 6 7 8 | @dataclass class Foo: settings: Settings pool: Pool @classmethod async def create(cls, settings: Settings, dsn): return cls(settings, await create_pool(dsn)) |