In Python, a context manager is an object that controls the environment in which a piece of code is executed. It provides a convenient way to manage resources such as file streams, network connections, and locks, which have certain states that need to be set up before the main code is run and cleaned up afterwards. For example, when you open a file using the with statement in Python, the open() function returns a context manager that is responsible for closing the file stream once the code inside the with block is finished executing. This helps to avoid resource leaks and makes your code more efficient and easier to read.
The context manager interface has two methods that must be implemented on an object that supports the interface, they are:
__enter__(): Executed prior to the code block.
__exit__(): Executed after the code block.
example of a custom context manager¶
class MyContextManager: def __init__(self, filename): self.filename = filename def __enter__(self): self.file = open(self.filename, 'r') return self.file def __exit__(self, exc_type, exc_val, exc_tb): self.file.close()
with MyContextManager('myfile.txt') as f: data = f.read() print(data)
Locks implement the context manager API and are compatible with the with statement. By using locks in the with statement, we do not need to explicitly acquire and release the lock:
import threading import logging logging.basicConfig(level=logging.DEBUG, format='(%(threadName)-10s) %(message)s',) def worker_with(lock): with lock: logging.debug('Lock acquired via with') def worker_not_with(lock): lock.acquire() try: logging.debug('Lock acquired directly') finally: lock.release() if __name__ == '__main__': lock = threading.Lock() w = threading.Thread(target=worker_with, args=(lock,)) nw = threading.Thread(target=worker_not_with, args=(lock,)) w.start() nw.start()