Vinn's Studio

Python Syntax: with

Word count: 600Reading time: 3 min
2020/04/16 Share

In this post, I will discuss the python syntax with. We can use this syntax in exception handling to make the code cleaner and much more readable. It simplifies the management of common resources like file streams.

##
Introduction

In python, we can use try and except as follows:

1
2
3
4
5
6
try: # Try to do something
do something
except: # If error occurs
do something
finally: # After that, clean and release the resource
do someting

For example:

1
2
3
4
5
6
7
try:
f = open('aFile.txt')
data = f.read()
except:
print('Fail to open')
finally:
f.close()

We can use a shorter code to the exact same thing:

1
2
with open('aFile.txt') as f:
data = f.read()

As we can see, with can make the code shorter.

##
Definitions

To use with in python, there are some definitions to understand:

Context Management Protocol

Context Management Protocol contains two methods: .__enter__() and .__exit__(). The former (.__enter__() method) is to allocate resource, while the latter (.__exit__() method) is to free the resource.

An object with these two methods can be processed by with.

Context Manager

An object with methods .__enter__() and .__exit__() is called context manager. For this example, the output of open('aFile.txt') (which is an object) is the context manager.

Context Expression

The syntax after with is called context expression. This syntax should return a context manager. For this example, open('aFile.txt') is a context expression.

With-body

The syntaxes wrapped up by with syntax are called with-body. For this example, data = f.read() is the with-body.

##
Usage

The standard format of with syntax is as follows:

1
2
with conetext_expression [as target(s)]:
# with-body code here

Then the process of with syntax is:

  1. Execute .__enter__() method, the output of this method will be assigned to variable target.
  2. Execute with-body syntaxes.
  3. Execute .__exit__() method.

An example can be seen as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Manager:
def __init__(self, name):
self.name = name
def __enter__(self):
print("[Enter %s]: Allocate resource." % self.name)
return self
def __exit__(self, exc_type, exc_value, exc_tb):
'''
exc_type: error(exception) type, None if no exception
exc_value: error(exception) value, None if no exception
exc_tb: error(exception) possion, None if no exception
'''
print("[Exit %s]: Free resource." % self.name)
if exc_tb is None:
print("[Exit %s]: Exited without exception." % self.name)
else:
print("[Exit %s]: Exited with exception raised." % self.name)
return False # Can be omitted, None is seen as False

If we execute the with code as follows:

1
2
with Manager('Amy') as test:
print("[with-body]: The name is %s." % test.name)

The output is as follows:

1
2
3
4
[Enter Amy]: Allocate resource.
[with-body]: The name is Amy.
[Exit Amy]: Free resource.
[Exit Amy]: Exited without exception.

If any exception raises, we can simulate this situation with code as follows:

1
2
3
with Manager('Amy') as test:
raise(Exception)
print("[with-body]: The name is %s." % test.name)

The output is as follows:

1
2
3
4
5
6
7
[Enter Amy]: Allocate resource.
[Exit Amy]: Free resource.
[Exit Amy]: Exited with exception raised.
Traceback (most recent call last):
...
File "...", line 27, in <module>
raise(Exception)
##
Function nested()

Module contextlib provided three objects, contextmanager, function nested() and closing. I will focus on nested() here.

Function nested() can organize many Context Managers together without nested syntaxes.

See this example:

1
2
with nested(A(), B(), C()) as (X, Y, Z):
# with-body code here

This is the same as:

1
2
3
4
with A() as X:
with B() as Y:
with C() as Z:
# with-body code here
CATALOG
  1. Context Management Protocol
  2. Context Manager
  3. Context Expression
  4. With-body