PEP-343: The with Statement
The with statement was introduced in Python 2.5 as a future feature (required "from future import with_statement"). It is now officially part of the language. The with statement facilitates acquiring and releasing a resource in a block of code. This is similar to the C++ RAII (Resource Acquisition Is Initialization), where a resource is acquired in the constructor of a local (on the stack) object and released in the destructor. It is useful for expensive resources that you want to release as soon as possible, without waiting for Python's GC to kick in. It also guarantees that the resource will be released. The syntax of the with statement is:
with EXPR as VAR:
BLOCK
The words with and as are now keywords (as is a "double" keyword also used in the try-except clause). EXPR is any expression that returns a context manager, which is an object that has __enter__ and __exit__ methods. The __enter__() method gets called before the BLOCK is executed and __exit__() gets called after the block is executed—even if block execution raises an exception. Here's a timing context manager example. This context manager prints the elapsed time for the block to execute:
import time
class timeit:
def __enter__(self):
self.start = time.time()
def __exit__(self, type, value, traceback):
print ('Elapsed:', time.time() - self.start)
You use it with the with keyword:
with timeit():
for i in range(1000):
open('1.txt', 'w',).write(str(i) * 2500)
Output:
Elapsed: 0.996018886566
Obviously, this is a very simple context manager; it ignores exceptions, and the code inside the block doesn't use the context manager itself. But that's not always the case. For example, the built-in open function (which is actually io.open as you may recall) is now a context manager that closes the file automatically when the scope is exited:
>>> with open('1.txt') as f:
... print('size of', f.name, 'is:', len(f.read()))
...
size of 1.txt is: 7500
>>> f
<io.TextIOWrapper object at 0xe9cd0>
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/
Versions/3.0/lib/python3.0/io.py", line 1721,
in read self._checkClosed()
File "/Library/Frameworks/Python.framework/
Versions/3.0/lib/python3.0/io.py", line 450,
in _checkClosed
if msg is None else msg)
ValueError: I/O operation on closed file.
This code illustrates a couple of interesting points:
- The open file is bound to the variable f.
- The file f was closed when the with clause finishes (hence the exception when trying to read it).
It is interesting that the variable f is still bound to the now closed file. It would have been nicer if the binding of the name f had been scoped to the block, so that trying to access f outside the with block would result in a NameError. I think this would better match the semantics of with, where resources exist only inside the with block.
Property Decorators
Property decorators help with writing properties. A property is one or more methods that can be accessed like a variable. Here is the classic property syntax as of Python 2.2:
class A:
def __init__(self):
self._x = 7
def _getX(self):
return self._x
def _setX(self, x):
assert isinstance(x, int)
self._x = x
def _delX(self, x):
del self._x
x = property(_getX, _setX, doc='x is an integer')
This class verifies that you set x to an int, and allows you to access x as a variable:
>>> a = A()
>>> a.x
7
>>> a.x = 5
>>> a.x
5
>>> a.x = 3.4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "article_4.py", line 27, in setX
assert isinstance(x, int)
AssertionError
You can even get help on x (via the class A and not the instance a):
>>> help(A.x)
Help on property:
x is an integer
All this is great, but properties do have some annoyances. First, you have to have a separate line to define the property, and second, there's no standard way to name the getter and setter (and deleter if you have one). The new property decorator solves these problems. Here's the same class using property decorators:
class A:
def __init__(self):
self._x = 7
@property
def x(self):
"""'x' is an integer"""
return self._x
@x.setter
def x(self, x):
assert isinstance(x, int)
self._x = x
@x.deleter
def x(self):
del self._x
Note that all the property accessors are now simply named x. The getter is decorated with @property and its doc string becomes the property docstring. The setter and deleter are decorated with @<property name>.setter and @<property name>.deleter respectively. To summarize, the property decorator is not earth-shattering but brings a little order and consistency to property definitions. I definitely plan to adopt this syntax for property definitions. The old syntax still works—and is necessary if you want to define a write-only property (no getter) with a doc string.