__init__ 中依赖协程的变量应该如何定义?

How should I define a variable that depends on a coroutine in __init__?

在classWizard中,我想将属性wand设置为协程返回的值magic

class Wizard:
    async def acquire_wand(self):
        self.wand = await magic()

此代码被视为 "bad Python",但是,因为 wand 未在 __init__ 中定义。不过,我无法在 __init__ 中定义它,因为 await 只能用于 asynchronous 函数。

class Wizard:
    def __init__(self):
        self.wand = None

    async def acquire_wand(self):
        self.wand = await magic()

    async def perform_spell(self):
        if self.wand is None:
            await self.acquire_wand()
        self.wand.wave()

我可以在 __init__ 中将 wand 设置为 None 并在任何访问它的地方使用 if self.wand is None: ,但这看起来很混乱和笨拙。

如何确保 wand 在整个 class 中定义?

将需要 self.wand 的函数包装在装饰器中,这将产生一个干净且可行的解决方案:

def with_wand(fn):
    def wrapper(self):
        if not self.wand:
            await self.acquire_wand()
        fn(self)
    return wrapper

@with_wand
async def perform_spell(self):
        self.wand.wave()

尚未测试代码,如果有效请告诉我们!

似乎使用以下方法是解决此问题的最佳方法。

class Wizard:
    def __init__(self):
        self.wand = None

    async def learn(self):
        self.wand = await magic()

    async def perform_spell(self):
        if self.wand is None:
            raise Exception("You must first learn to use a wand!")
        self.wand.wave()

技术上有一个重写 __new__ 方法的技巧:

class InitCoroMixin:
    """ Mixin for create initialization coroutine
    """
    def __new__(cls, *args, **kwargs):
        """ This is magic!
        """
        instance = super().__new__(cls)

        @asyncio.coroutine
        def coro():
            instance.__init__(*args, **kwargs)
            yield from instance.__ainit__()
            return instance

        return coro()

    @asyncio.coroutine
    def __ainit__(self):
        raise NotImplementedError

查看 aiohttp_traversal 完整示例代码。

但我强烈反对这种方法:在构造函数中使用 I/O 通常不是一个好主意,请考虑一下。

我想你得到了你的建议,但我想质疑你的前提。谁告诉你是"considered bad Python"?每当我需要它们记住某些东西时,我总是给我的对象赋予属性。他们有 __dict__ 是有原因的。 Python 不是 Java。