我使用了一些第三方代码,可以通过@contextlib.contextmanager
修饰的例程启用锁定。我还使用大型python3代码库,我们可以插入不同的锁定软件,只要我能够实现acquire
和release
方法。
我试图在这个软件结构中使用第三方代码(不知道它是如何编写的)。
为了澄清我正在寻找的内容,假设其中一个第三方锁定例程被编写为标准@contextlib.contextmanager
生成器,如下所示:
@contextlib.contextmanager
def lock(arg0, arg1):
try:
# This section of code corresponds to `acquire`.
# Acquire a lock called 'lock', and then ...
yield lock
finally:
# This section of code coresponds to `release`.
# Do cleanup.
通常会像这样使用......
with third.party.lock(arg0, arg1):
# Do stuff in this critical section
但正如我上面提到的,我想编写一个具有acquire
方法和release
方法的类,它使用third.party.lock
,我想要通过现有的third.party.lock
模块完成,无需重写。
换句话说,我想写一个看起来像这样的课......
class LockWrapper(object):
def __init__(self):
# initialization
def acquire(self):
# Use third.party.lock to obtain a lock.
# ??? ??? ???
def release(self):
# I don't know what to do here. There is no yield
# in the `finally` section of a normal
# @contextlib.contextmanager decorated method/
# ??? ??? ???
正如我在示例代码的评论中所述,我不知道如何让acquire
和release
做任何有意义的事情。
看起来我必须从最初的third.party.lock
模块中窃取代码才能实现这一目标,但我希望我能够在不必知道的情况下忽略这样做的方法有关此第三方代码的任何信息。
我运气不好吗?
非常感谢。
答案 0 :(得分:0)
好的,我明白了。我的答案基于这里的一些代码:https://gist.github.com/icio/c0d3f7efd415071f725b
关键是这个enter_context
辅助函数,它深入研究了上下文管理器的结构。它类似于该网站上同名的功能......
def enter_context(func, *args, **kwargs):
def _acquire():
with func(*args, **kwargs) as f:
yield f
acquire_gen = _acquire()
def release_func():
try:
next(acquire_gen)
except StopIteration:
pass
return acquire_gen, release_func
以下类为任何传递给构造函数的contextmanager函数实现acquire
和release
。我将enter_context
封装在这个类中......
class ContextWrapper(object):
@staticmethod
def enter_context(func, *args, **kwargs):
def _acquire():
with func(*args, **kwargs) as f:
yield f
acquire_gen = _acquire()
def release_func():
try:
next(acquire_gen)
except StopIteration:
pass
return acquire_gen, release_func
def __init__(self, func, *args, **kwargs):
self._acq, self._rel = self.__class__.enter_context(func, *args, **kwargs)
def acquire(self):
next(self._acq)
return True
def release(self):
self._rel()
# Traditional Dijkstra names.
P = acquire
V = release
然后,我可以在third.party.lock
周围创建我的包装器,如下所示......
mylocker = ContextWrapper(third.party.lock, arg0, arg1)
...我可以按照以下方式致电acquire
和release
......
mylocker.acquire()
# or mylocker.P()
mylocker.release()
# or mylocker.V()
这是另一个如何使用这个类的例子......
class ThirdPartyLockClass(ContextWrapper):
def __init__(self, arg0, arg1):
# do any initialization
super().__init__(third.party.lock, arg0, arg1)
# Implementing the following methods is optional.
# This is only needed if this class intends to do more
# than the wrapped class during `acquire` or `release`,
# such as logging, etc.
def acquire(self):
# do whatever
rc = super().acquire()
# maybe to other stuff
return rc
def release(self):
# do whatever
super().release()
# maybe do other stuff
P = acquire
V = release
mylocker = ThirdPartyLockClass(arg0, arg1)
mylocker.acquire()
# or mylocker.P()
mylocker.release()
# or mylocker.V()
...或者我甚至可以在没有额外功能添加到第三方锁类的简单情况下执行以下操作...
class GenericLocker(ContextWrapper):
def __init__(self, func, *args, **kwargs):
super().__init__(func, *args, **kwargs)
mylocker = GenericLocker(third.party.lock, arg0, arg1)
mylocker.acquire()
# or mylocker.P()
mylocker.release()
# or mylocker.V()
答案 1 :(得分:0)
为什么不明确调用contextlib包装器的__enter__
和__exit__
?
import contextlib
# Something to wrap
class Test:
def __init__(self):
print('Opening Test')
def use(self):
print('Using Test')
def close(self):
print('Closing Test')
# Wrap in the standard contextlib
@contextlib.contextmanager
def test():
try:
t = Test()
yield t
finally:
t.close()
# Does it work? yes...
with test() as t:
t.use()
# Implementing acquire/release...
class TestWrapper(object):
def __init__(self):
pass
def acquire(self):
self.t = test()
return self.t.__enter__()
def release(self):
self.t.__exit__(None,None,None)
# Also works...
tw = TestWrapper()
t = tw.acquire()
t.use()
tw.release()
输出:
Opening Test
Using Test
Closing Test
Opening Test
Using Test
Closing Test