
Writing Good APIs With Context Managers
Writing Good APIs With Context Managers 관련


Context managers are quite flexible, and if you use the with
statement creatively, then you can define convenient APIs for your classes, modules, and packages.
For example, what if the resource you wanted to manage is the text indentation level in some kind of report generator application? In that case, you could write code like this:
with Indenter() as indent:
indent.print("hi!")
with indent:
indent.print("hello")
with indent:
indent.print("bonjour")
indent.print("hey")
This almost reads like a domain-specific language (DSL) for indenting text. Also, notice how this code enters and leaves the same context manager multiple times to switch between different indentation levels. Running this code snippet leads to the following output and prints neatly formatted text:
hi!
hello
bonjour
hey
How would you implement a context manager to support this functionality? This could be a great exercise to wrap your head around how context managers work. So, before you check out the implementation below, you might take some time and try to solve this by yourself as a learning exercise.
Ready? Here’s how you might implement this functionality using a context manager class:
class Indenter:
def __init__(self):
self.level = -1
def __enter__(self):
self.level += 1
return self
def __exit__(self, exc_type, exc_value, exc_tb):
self.level -= 1
def print(self, text):
print(" " * self.level + text)
Here, .__enter__()
increments .level
by 1
every time the flow of execution enters the context. The method also returns the current instance, self
. In .__exit__()
, you decrease .level
so the printed text steps back one indentation level every time you exit the context.
The key point in this example is that returning self
from .__enter__()
allows you to reuse the same context manager across several nested with
statements. This changes the text indentation level every time you enter and leave a given context.
A good exercise for you at this point would be to write a function-based version of this context manager. Go ahead and give it a try!