2016-06-05 4 views
1

В классе Wizard, я хотел бы установить атрибут wand на значение, возвращаемое coroutine magic.Как определить переменную, которая зависит от сопрограммы в __init__?

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

Этот код считается "плохим Python", однако, поскольку wand не определен в __init__. Я не могу определить его в __init__, хотя, поскольку await может использоваться только в async.

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 определен во всем классе?

ответ

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 код для полного примера.

Но я очень обескураживаю метод: наличие ввода-вывода в конструкторе - это, как правило, плохая идея, подумайте об этом.

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

не тестировал код, дайте нам знать, если он работает!

+0

Это работает, но может быть довольно беспорядочным, если у меня есть несколько атрибутов ('wand',' cloak', 'hat' и' rabbit'). Это также может означать, что для каждого атрибута, который я использую в функции, мне понадобится '@ decorator', который кажется неудобным. – 2Cubed

+0

@ 2Cubed У вас может быть декоратор, который принимает ** необязательные ** аргументы и проверяет, существуют ли все эти 'свойства'! – j4hangir

+0

Jahangir: Верно. Но это по-прежнему кажется беспорядочным способом - я бы предпочел не использовать один и тот же декоратор для каждой функции в классе. – 2Cubed

0

Похоже, что использование этой проблемы является наилучшим способом.

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

Я думаю, вы получили свой совет, но я хотел бы расспросить ваше помещение. Кто вам сказал, что это «плохой питон»? Я постоянно даю атрибуты объектов, когда мне нужно, чтобы они что-то запоминали. У них есть __dict__ с. Python - это не Java.

+0

Обычно считается, что Python не определяет атрибут в '__init__'. Если вы (или пользователь вашего класса) решили получить доступ к атрибуту 'self.wand', он поднимет атрибут AttributeError, который не является« хорошим ». – 2Cubed

+0

У вас есть ссылка на то, что «вообще считается плохой» ??Я раньше этого не видел, кроме как в письменной форме людей, которые считают, что Python - это просто вариант Java. И, конечно же, вы не будете иметь доступ к self.wand, если у вас нет палочки, так же, как вы не получаете доступ к self.boat, если у вас нет лодки. Какой смысл? '__init__' не имеет к этому никакого отношения. Если вы хотите узнать, какие атрибуты у объекта есть, используйте dir (или программно, используйте hasattr/getattr). – Veky

+0

http://stackoverflow.com/questions/19284857/instance-attribute-attribute-name-defined-outside-init, http://pylint-messages.wikidot.com/messages:w0201 – 2Cubed

Смежные вопросы