Python 中的简单上下文管理器

Trivial context manager in Python

我的资源可以是需要锁定的 R1 类型或 R2 类型 不需要它:

class MyClass(object):   # broken
    def __init__ (self, ...):
        if ...:
            self.resource = R1(...)
            self.lock = threading.Lock()
        else:
            self.resource = R2(...)
            self.lock = None

    def foo(self):   # there are many locking methods
        with self.lock:
            operate(self.resource)

如果 self.lockNone,上述显然失败了。

我的选择是:

  1. if:

    def foo(self):
        if self.lock:
            with self.lock:
                operate(self.resource)
        else:
            operate(self.resource)
    
    • 缺点:太冗长
    • pro: 不会创建不必要的 threading.Lock
  2. 始终将 self.lock 设置为 threading.Lock

    • 亲:代码被简化了
    • 缺点:with self.lock 看起来比较贵 (媲美盘i/o!)
  3. 定义一个简单的锁class:

    class TrivialLock(object):
        def __enter__(self): pass
        def __exit__(self, _a, _b, _c): pass
        def acquire(self): pass
        def release(self): pass
    

    并使用它代替 None 用于 R2

    • 专业版:简单代码
    • 缺点:我必须定义 TrivialLock

问题

  1. 社区更喜欢哪种方法?
  2. 不管 (1),有没有人真正定义类似 TrivialLock? (我实际上期望类似的东西会是 在标准库中...)
  3. 我观察到锁定成本与 write符合预期?

我会定义TrivialLock。不过,它可能更简单,因为您只需要一个上下文管理器,而不是锁。

class TrivialLock(object):
    def __enter__(self):
        pass
    def __exit__(*args):
        pass

您可以使用 contextlib:

使这更简单
import contextlib

@contextlib.contextmanager
def TrivialLock():
    yield

self.lock = TrivialLock()

并且因为 yield 可以是一个表达式,所以你可以定义 TrivalLock inline 代替:

self.lock = contextlib.contextmanager(lambda: (yield))()

注意括号; lambda: yield 无效。但是,生成器表达式 (yield) 使它成为 single-use 上下文管理器;如果您尝试在第二个 with 语句中使用相同的值,则会收到 Runtime 错误,因为 generator 已用尽。