我应该如何在__init__中定义一个依赖于协程的变量?

时间:2016-06-05 15:29:50

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

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

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

但是,此代码被视为“糟糕的Python”,因为wand中未定义__init__。但是,我无法在__init__中对其进行定义,因为await只能在async hronous函数中使用。

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()

我可以在wand中将None设置为__init__,并在访问它的任何地方使用if self.wand is None:,但这看起来很麻烦且不实用。

如何确保在整个课程中定义wand

4 个答案:

答案 0 :(得分:5)

从技术上讲,有一个覆盖__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通常是一个坏主意,请考虑一下。

答案 1 :(得分:1)

在装饰器中包含需要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()

尚未测试代码,请告知我们是否有效!

答案 2 :(得分:0)

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

?substitute

答案 3 :(得分:0)

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