Несколько способов вызвать контекстный менеджер в Python - PullRequest
0 голосов
/ 29 ноября 2018

Фон

У меня есть класс в Python, который принимает список мьютексов.Затем он сортирует этот список и использует __enter__() и __exit__() для блокировки / разблокировки всех мьютексов в определенном порядке для предотвращения взаимных блокировок.

В настоящее время класс избавляет нас от множества потенциальных блокировок,поскольку мы можем просто вызывать его в RAII стиле , то есть:

self.lock = SuperLock(list_of_locks)
# Lock all mutexes.
with self.lock:
    # Issue calls to all hardware protected by these locks.

Задача

Мы хотели бы раскрыть способы для этого класса предоставитьAPI в стиле RAII, поэтому мы можем заблокировать только половину мьютексов за один раз, когда вызывается определенным образом, например:

self.lock = SuperLock(list_of_locks)
# Lock all mutexes.
with self.lock:
    # Issue calls to all hardware protected by these locks.

# Lock the first half of the mutexes in SuperLock.list_of_locks
with self.lock.first_half_only:
    # Issue calls to all hardware protected by these locks.

# Lock the second half of the mutexes in SuperLock.list_of_locks
with self.lock.second_half_only:
    # Issue calls to all hardware protected by these locks.

Вопрос

Есть ли способпредоставить такой тип функциональности, чтобы я мог вызывать with self.lock.first_half_only или with self.lock_first_half_only(), чтобы предоставить пользователям простой API?Мы хотели бы сохранить все эти функции в одном классе.

Спасибо.

Ответы [ 3 ]

0 голосов
/ 29 ноября 2018
from contextlib import contextmanager

class A:

    @contextmanager
    def i_am_lock(self):
        print("entering")
        yield
        print("leaving")

a = A()

with a.i_am_lock():
    print("inside")

Вывод:

entering
inside
leaving

Далее вы можете использовать contextlib.ExitStack для лучшего управления вашими замками.

0 голосов
/ 29 ноября 2018

Я бы использовал SimpleNamespace, чтобы разрешить доступ к атрибутам для различных SuperLock объектов, например:

from types import SimpleNamespace

self.lock = SimpleNamespace(
    all=SuperLock(list_of_locks),
    first_two_locks=SuperLock(list_of_locks[:2]),
    other_locks=SuperLock(list_of_locks[2:])
)

with self.lock.all:
    # Issue calls to all hardware protected by these locks.

with self.lock.first_two_locks:
    # Issue calls to all hardware protected by these locks.

with self.lock.other_locks:
    # Issue calls to all hardware protected by these locks.

Редактировать:

Для Python 2Вы можете использовать этот класс для достижения аналогичного поведения:

class SimpleNamespace:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
0 голосов
/ 29 ноября 2018

Да, вы можете получить этот интерфейс.Объект, который будет введен / завершен в контексте оператора with, является разрешенным атрибутом.Таким образом, вы можете пойти дальше и определить менеджеры контекста как атрибуты вашего менеджера контекста:

from contextlib import ExitStack  # pip install contextlib2
from contextlib import contextmanager

@contextmanager
def lock(name):
    print("entering lock {}".format(name))
    yield
    print("exiting lock {}".format(name))

@contextmanager
def many(contexts):
    with ExitStack() as stack:
        for cm in contexts:
            stack.enter_context(cm)
        yield

class SuperLock(object):

    def __init__(self, list_of_locks):
        self.list_of_locks = list_of_locks

    def __enter__(self):
        # implement for entering the `with self.lock:` use case
        return self

    def __exit__(self, exce_type, exc_value, traceback):
        pass

    @property
    def first_half_only(self):
        return many(self.list_of_locks[:4])

    @property
    def second_half_only(self):
        # yo dawg, we herd you like with-statements
        return many(self.list_of_locks[4:])

Когда вы создаете и возвращаете новый менеджер контекста, вы можете использовать состояние из экземпляра (то есть self).

Пример использования:

>>> list_of_locks = [lock(i) for i in range(8)] 
>>> super_lock = SuperLock(list_of_locks) 
>>> with super_lock.first_half_only: 
...     print('indented') 
...   
entering lock 0
entering lock 1
entering lock 2
entering lock 3
indented
exiting lock 3
exiting lock 2
exiting lock 1
exiting lock 0

Редактировать : основанный на классе эквивалент менеджера контекста генератора lock, показанного выше

class lock(object):

    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print("entering lock {}".format(self.name))
        return self

    def __exit__(self, exce_type, exc_value, traceback):
        print("exiting lock {}".format(self.name))
        # If you want to handle the exception (if any), you may use the
        # return value of this method to suppress re-raising error on exit
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...