With: Context Managers

Basic concept

with (or with...as...) is actually a special sign that tells the interpreter to call a certain function when entering with block and call another function when leaving with block.

Since it calls a function, it will get return object. We can use with...as... to catch this object.

Context manager classes

For each class that implements __enter__ and __exit__, it is a context manager.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class CTManager:
def __init__(self):
print('__init__ runs')

def __enter__(self):
print('__enter__ runs')
return self

def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__ runs')

def print_message(self):
print('this is a context manager')

ct = CTManager()

print('with block')

with ct as c:
c.print_message()
# with CTManager() as c:
# c.print_message()
# is also valid
'''
We get:
__init__ runs
with block
__enter__ runs
this is a context manager
__exit__ runs
'''

For a context manager class, if it hasn't been initialized, __init__ will be called. Then __enter__ will be implemented automatically and __exit__ will be implemented when leaving the block.

Context manager functions

The definition of context manager functions is much easier than context manager classes since it is almost the same as defining a generator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import contextlib

@contextlib.contextmanager
def ctmanager():
# __enter__()
print("enter")
yield
# __exit__()
print('exit')

with ctmanager():
print('main')

'''
We get:
enter
main
exit
'''

Before creating a context manager function, we should import contextlib and pass the context manager function as a parameter to contextlib.contextmanager.

@ has two meanings in python:

  • Matrix multiplication A @ B;
  • Function modifier: Pass the function defined below it as a parameter of the function behind it.

When with a context manager function, the function will keep running to yield. Then, finish the code left when with block finishs.