在 python 中调用上下文管理器的多种方法
Multiple ways to invoke context manager in python
背景
我在 python 中有一个 class 接受互斥体列表。然后它对该列表进行排序,并使用 __enter__()
和 __exit__()
以特定顺序 lock/unlock 所有互斥量以防止死锁。
class 目前为我们节省了很多潜在死锁的麻烦,因为我们可以在 RAII style 中调用它,即:
self.lock = SuperLock(list_of_locks)
# Lock all mutexes.
with self.lock:
# Issue calls to all hardware protected by these locks.
问题
我们想公开此 class 提供 RAII 样式 API 的方法,以便我们可以一次只锁定一半的互斥体,当以某种方式调用时,即:
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给用户?我们希望将所有这些功能保留在一个 class.
中
谢谢。
是的,你可以得到这个界面。在 with 语句的上下文中将 entered/exited 的对象是已解析的属性。因此,您可以继续将上下文管理器定义为上下文管理器的属性:
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:])
当您创建并 return 一个新的上下文管理器时,您可以使用实例中的状态(即 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
编辑:基于 class 等同于上面显示的 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
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
更好地管理您的锁。
我会使用 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 来实现类似的行为:
class SimpleNamespace:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
背景
我在 python 中有一个 class 接受互斥体列表。然后它对该列表进行排序,并使用 __enter__()
和 __exit__()
以特定顺序 lock/unlock 所有互斥量以防止死锁。
class 目前为我们节省了很多潜在死锁的麻烦,因为我们可以在 RAII style 中调用它,即:
self.lock = SuperLock(list_of_locks)
# Lock all mutexes.
with self.lock:
# Issue calls to all hardware protected by these locks.
问题
我们想公开此 class 提供 RAII 样式 API 的方法,以便我们可以一次只锁定一半的互斥体,当以某种方式调用时,即:
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给用户?我们希望将所有这些功能保留在一个 class.
谢谢。
是的,你可以得到这个界面。在 with 语句的上下文中将 entered/exited 的对象是已解析的属性。因此,您可以继续将上下文管理器定义为上下文管理器的属性:
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:])
当您创建并 return 一个新的上下文管理器时,您可以使用实例中的状态(即 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
编辑:基于 class 等同于上面显示的 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
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
更好地管理您的锁。
我会使用 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 来实现类似的行为:
class SimpleNamespace:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)