Предотвратить двойной вызов метода в классе, который можно вызвать с помощью «with» - PullRequest
0 голосов
/ 02 мая 2018

Я пишу класс, который можно использовать с оператором with, например:

with Context() as context:
    if context:
        ...

Класс имеет функцию enter, которая должна вызываться только один раз и возвращает логическое значение. Я хочу предотвратить повторный вызов (например, with Context().enter() as context):

class Context(object):
    def __init__(self):
        self.ENTER_HAS_BEEN_CALLED = False

    def __enter__(self):
        return self.enter()

    def __exit__(self):
        self.exit()

    def enter(self):
        """Do things once and only once
        Returns boolean, not self
        """
        if self.ENTER_HAS_BEEN_CALLED: 
            # not sure what to do here, return?
            # call self.exit()?

        self.ENTER_HAS_BEEN_CALLED = True

        # do things that should only be done once
        value = True # or False
        return value

    def exit(self):
        pass

Это правильный способ предотвратить повторный вызов функции? Я хочу, чтобы возвращаемое значение, а также чтобы этот код работал:

context_manager = Context()
context = context_manager.enter()
if context: ...
context_manager.exit()

1 Ответ

0 голосов
/ 02 мая 2018

Вы близки, однако вы можете просто вызвать ошибку, если contextmanager уже был вызван, после установки флага с помощью декоратора. Приведенный ниже код написан так, что и enter, и __enter__ могут возвращать отдельные переменные (True и self), как предполагалось изначально и в соответствии с общей идеей управления контекстом:

def control_manager(f):
   def wrapper(cls):
      if getattr(cls, 'flag'):
        raise Exception("Already expended the context manager")
      setattr(cls, 'flag', True)
      return f(cls)
   return wrapper

class Context:
   def __init__(self):
     self.flag = False
   @control_manager
   def __enter__(self):
     return self
   def __exit__(self, *args):
     pass
   @control_manager
   def enter(self):
     return True
   def exit(self):
     #do something
     pass

with Context() as f:
 v = f.enter()

Traceback (последний вызов был последним): Файл "", строка 2, в Файл "", строка 4, в обёртке Исключение: уже израсходован менеджер контекста

Однако, это будет работать на втором тесте:

c = Context()
v = c.enter()
c.exit()

Однако, если вы хотите использовать enter в качестве основного блока для менеджера контекста в классе, вы можете рассматривать его как classmethod с contextlib.contextmanager:

import contextlib

class Control:
   flag = False
   def __init__(self):
     pass
   def __enter__(self):
     with Control.enter() as f:
       v = f
     return self
   def __exit__(self, *args):
     pass
   @classmethod
   @control_manager
   @contextlib.contextmanager
   def enter(cls):
     yield True


with Control() as f:
  pass
#runs without exception

with Control.enter() as t:
  pass

Traceback (последний вызов был последним): Файл "", строка 1, в Файл "", строка 4, в обёртке Исключение: уже израсходован менеджер контекста

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...