sds
sds

Reputation: 60014

Trivial context manager in Python

My resource can by of type R1 which requires locking or of type R2 which does not require it:

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)

The above obviously fails if self.lock is None.

My options are:

  1. if:

    def foo(self):
        if self.lock:
            with self.lock:
                operate(self.resource)
        else:
            operate(self.resource)
    
    • cons: too verbose
    • pro: does not create an unnecessary threading.Lock
  2. always set self.lock to threading.Lock

    • pro: code is simplified
    • cons: with self.lock appears to be relatively expensive (comparable to disk i/o!)
  3. define a trivial lock class:

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

    and use it instead of None for R2.

    • pro: simple code
    • cons: I have to define TrivialLock

Questions

  1. What method is preferred by the community?
  2. Regardless of (1), does anyone actually define something like TrivialLock? (I actually expected that something like that would be in the standard library...)
  3. Is my observation that locking cost is comparable to that of a write conforms to expectations?

Upvotes: 3

Views: 746

Answers (1)

chepner
chepner

Reputation: 531055

I would define TrivialLock. It can be even more trivial, though, since you just need a context manager, not a lock.

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

You can make this even more trivial using contextlib:

import contextlib

@contextlib.contextmanager
def TrivialLock():
    yield

self.lock = TrivialLock()

And since yield can be an expression, you can define TrivalLock inline instead:

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

Note the parentheses; lambda: yield is invalid. However, the generator expression (yield) makes this a single-use context manager; if you try to use the same value in a second with statement, you get a Runtime error because the generator is exhausted.

Upvotes: 3

Related Questions