Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


A Developer's Guide to Python 3.0: Package Installation and More Language Enhancements : Page 2

Explore Python 3.0's new support for per-user installations, an official with statement, property decorators, keyword-only arguments, dictionary changes, and C API changes.


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:


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.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.