Learning Python - Mark Lutz [71]
This also means that any list comprehension expression, such as this one, which computes the squares of a list of numbers:
>>> squares = [x ** 2 for x in [1, 2, 3, 4, 5]]
>>> squares
[1, 4, 9, 16, 25]
can always be coded as an equivalent for loop that builds the result list manually by appending as it goes:
>>> squares = []
>>> for x in [1, 2, 3, 4, 5]: # This is what a list comprehension does
squares.append(x ** 2) # Both run the iteration protocol internally
>>> squares
[1, 4, 9, 16, 25]
The list comprehension, though, and related functional programming tools like map and filter, will generally run faster than a for loop today (perhaps even twice as fast)—a property that could matter in your programs for large data sets. Having said that, though, I should point out that performance measures are tricky business in Python because it optimizes so much, and performance can vary from release to release.
A major rule of thumb in Python is to code for simplicity and readability first and worry about performance later, after your program is working, and after you’ve proved that there is a genuine performance concern. More often than not, your code will be quick enough as it is. If you do need to tweak code for performance, though, Python includes tools to help you out, including the time and timeit modules and the profile module. You’ll find more on these later in this book, and in the Python manuals.
Missing Keys: if Tests
One other note about dictionaries before we move on. Although we can assign to a new key to expand a dictionary, fetching a nonexistent key is still a mistake:
>>> D
{'a': 1, 'c': 3, 'b': 2}
>>> D['e'] = 99 # Assigning new keys grows dictionaries
>>> D
{'a': 1, 'c': 3, 'b': 2, 'e': 99}
>>> D['f'] # Referencing a nonexistent key is an error
...error text omitted...
KeyError: 'f'
This is what we want—it’s usually a programming error to fetch something that isn’t really there. But in some generic programs, we can’t always know what keys will be present when we write our code. How do we handle such cases and avoid errors? One trick is to test ahead of time. The dictionary in membership expression allows us to query the existence of a key and branch on the result with a Python if statement (as with the for, be sure to press Enter twice to run the if interactively here):
>>> 'f' in D
False
>>> if not 'f' in D:
print('missing')
missing
I’ll have much more to say about the if statement and statement syntax in general later in this book, but the form we’re using here is straightforward: it consists of the word if, followed by an expression that is interpreted as a true or false result, followed by a block of code to run if the test is true. In its full form, the if statement can also have an else clause for a default case, and one or more elif (else if) clauses for other tests. It’s the main selection tool in Python, and it’s the way we code logic in our scripts.
Still, there are other ways to create dictionaries and avoid accessing nonexistent keys: the get method (a conditional index with a default); the Python 2.X has_key method (which is no longer available in 3.0); the try statement (a tool we’ll first meet in Chapter 10 that catches and recovers from exceptions altogether); and the if/else expression (essentially, an if statement squeezed onto a single line). Here are a few examples:
>>> value = D.get('x', 0) # Index but with a default
>>> value
0
>>> value = D['x'] if 'x' in D else 0 # if/else expression form
>>> value
0
We’ll save the details on such alternatives until a later chapter. For now, let’s move on to tuples.
* * *
[14] Keep in mind that the rec record we just created really could be a database record, when