The with statement¶
The with statement is used to wrap the execution of a block with methods defined by a context manager (see section With Statement Context Managers). This allows common try…except…finally usage patterns to be encapsulated for convenient reuse.
with_stmt ::= “with” with_item (“,” with_item)* “:” suite
with_item ::= expression [“as” target]
The execution of the with statement with one “item” proceeds as follows:
1, The context expression (the expression given in the with_item) is evaluated to obtain a context manager.
2, The context manager’s __exit__() is loaded for later use.
3, The context manager’s __enter__() method is invoked.
4, If a target was included in the with statement, the return value from __enter__() is assigned to it.
5, The suite is executed.
6, The context manager’s __exit__() method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to __exit__(). Otherwise, three None arguments are supplied.
!Note: If the suite was exited due to an exception, and the return value from the __exit__() method was false, the exception is reraised. If the return value was true, the exception is suppressed, and execution continues with the statement following the with statement.
- Specification: The 'with' Statement¶
with EXPR as VAR:
BLOCK
The translation of the above statement is:
mgr = (EXPR)
exit = type(mgr).__exit__ # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
try:
VAR = value # Only if "as VAR" is present
BLOCK
except:
# The exceptional case is handled here
exc = False
if not exit(mgr, *sys.exc_info()):
raise
# The exception is swallowed if exit() returns true
finally:
# The normal and non-local-goto cases are handled here
if exc:
exit(mgr, None, None, None)
for example
- 1
file = open("/tmp/foo.txt")
try:
data = file.read()
finally:
file.close()
is the same as:
with open("/tmp/foo.txt") as file:
data = file.read()
@contextmanager
def opening(filename):
f = open(filename)
try:
yield f
finally:
f.close()
with opening(filename) as file:
data = file.read()
- 2
class Sample:
def __enter__(self):
return self
def __exit__(self, type, value, trace):
print "type:", type
print "value:", value
print "trace:", trace
def do_something(self):
bar = 1/0
return bar + 10
with Sample() as sample:
sample.do_something()
its output
type: <type 'exceptions.ZeroDivisionError'>
value: integer division or modulo by zero
trace: <traceback object at 0x1004a8128>
Traceback (most recent call last):
File "./with_example02.py", line 19, in <module>
sample.do_something()
File "./with_example02.py", line 15, in do_something
bar = 1/0
ZeroDivisionError: integer division or modulo by zero
- 3
class DatabaseConnection:
# Database interface
def cursor(self):
"Returns a cursor object and starts a new transaction"
def commit(self):
"Commits current transaction"
def rollback(self):
"Rolls back current transaction"
...
def __enter__(self):
# Code to start a new transaction
cursor = self.cursor()
return cursor
...
def __exit__(self, type, value, tb):
if tb is None:
# No exception, so commit
self.commit()
else:
# Exception occurred, so rollback.
self.rollback()
# return False
db_connection = DatabaseConnection()
with db_connection as cursor:
cursor.execute('insert into ...')
cursor.execute('delete from ...')
# ... more operations ...
is the same as
from contextlib import contextmanager
@contextmanager
def db_transaction(connection):
cursor = connection.cursor()
try:
yield cursor
except:
connection.rollback()
raise
else:
connection.commit()
db = DatabaseConnection()
with db_transaction(db) as cursor:
...