contextmanager

./code/context_manager/main.py
 1#!/usr/bin/env python3
 2
 3import contextlib
 4from contextlib import contextmanager
 5
 6number = 10
 7
 8
 9def get_number():
10    return number
11
12
13def set_number(n):
14    global number
15    number = n
16
17
18@contextmanager
19def process(new_n):
20    old_n = get_number()
21    set_number(new_n)
22    try:
23        yield
24    finally:
25        set_number(old_n)
26
27
28print(process(1))  # <contextlib._GeneratorContextManager object at 0x108a2ccd0>
29print(type(process(1)))  # <class 'contextlib._GeneratorContextManager'>
30assert isinstance(process(1), contextlib._GeneratorContextManager), type(process(1))
31
32with process(100):
33    # inside this block ,number is changed to 100
34    assert number == 100, number
35
36# outside of the above block, number is reset to its original value
37assert number == 10, number
38
39
40class MyProcess:
41    def __init__(self, new_n):
42        self.new_n = new_n
43
44    def __enter__(self):
45        self.old_n = get_number()
46        set_number(self.new_n)
47
48    def __exit__(self, type, value, traceback):
49        set_number(self.old_n)
50
51
52with MyProcess(-1):
53    assert number == -1, number
54
55assert number == 10, number

See also https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager

To manipulate a lock:

@contextmanager
def locked(lock):
  lock.acquire()
  try:
    yield
  finally:
    lock.release()

with locked(lock):
  pass

Or use a class:

class locked:
  def __init__(self, lock):
    self.lock = lock

  def __enter__(self):
    self.lock.acquire()

  def __exit__(self, type, value, traceback):
    self.lock.release()