Common Python Idioms
Use
of programming language idioms makes your code more concise and
improves readability. It is often necessary to know the idioms in order
to understand what somebody else's code is doing. The examples below
illustrate the most common python idioms.
Swapping Values
Swapping
values of two variables can be accomplished by a single assignment. For
example, to swap values of variables named "a" and "b" use a, b = b, a. The following code snippet illustrates this idiom. Copy/paste it into the python prompt:
a = 1
b = 2
a, b = b, a
print "a is", a, " b is", b
You can do it with as many items as you like:
a, b, c, d = b, c, d, a
maps b into a, c into b, etc. Can you explain why this idiom works? (Hint: think about tuple packing and unpacking.)
Simple Loops
Looping
from 0 to n (n itself is excluded) in the increasing order is
accomplished by using the "xrange" function inside the "for" statement:
n = 10
for i in xrange(n):
# Do something, for example, print i
print "i is", i
Here,
the "xrange" function is used just like "range" (and it has the same
arguments), but it works faster. This is because "xrange" does not
build the whole sequence: it just returns elements one at a time when
they are needed (technically, "xrange" is a generator).
"xrange" is sometimes useful for building infinite loops. For almost all practical purposes,
for i in xrange(2**31-1):
is equivalent to
while 1:
except
that the "for" loop also initializes the "i" variable to the cycle
number at the beginning of each cycle. Note that using the "range"
function in this situation instead of "xrange" would be a bad
idea: "range" would attempt to build a complete sequence with 231 - 1 elements and would probably overflow the memory of your computer.
For people familiar with C/C++: note that pythonic "for" does not
work like in C/C++. Instead, its behavior is more similar to the
"for_each" function from the C++ standard library. Here is the
illustration:
n = 10
for i in xrange(n):
print "i is", i
i += 1
The
snippet above produces identical output to the snippet at the
beginning of this subsection because "i" is overwritten at the beginning of
each cycle. In C, the effect of incrementing "i" inside the loop body would
result in skipping every second printout.
Using Tuples/Lists instead of Simple Classes
In
simple cases, you can often avoid writing your own classes by using
Python's heterogeneous containers instead. Lists or tuples with small
number of elements allow for convenient
packing/unpacking on-the-fly. Example:
course_info = ("Computational Physics", "SCI 29", "TTH 14:00 - 15:20")
name, room, time = course_info
print "My", name, "course", "meets", time, "at", room
This
technique works especially well if your code makes a good use of
built-in functions for container look-up and manipulation. In this case
you can save the writing of quite a few methods in a class.
Iterating over Nested Sequences
When
you use tuples or lists instead of simple classes, looping
over sequences of such tuples can be combined with unpacking:
course_info = (("Computational Physics", "SCI 29", "TTH 14:00 - 15:20"),
("Advanced Quantum Mechanics", "SCI 10", "MWF 9:00 - 9:45"))
for (name, room, time) in course_info:
print "My", name, "course", "meets", time, "at", room
This "for" loop is equivalent to (but slightly faster than)
for info in course_info:
name, room, time = info
print "My", name, "course", "meets", time, "at", room
Iterating over Multiple Sequences
Simultaneous iteration over multiple sequences can be accomplished using the "zip" function. Example:
x_sequence = (1, 2, 3, 4)
y_sequence = (5, 6, 7)
z_sequence = (8, 9)
for x, y, z in zip(x_sequence, y_sequence, z_sequence):
print "x is", x, " y is", y, " z is", z
Note that the iteration terminates as soon as the shortest sequence runs out of elements.
Default Parameter Values
Be
careful while assigning default values to function parameters. In
particular, using a mutable object as a default value is usually not
the intended thing to do. A typical mistake is initializing a list
argument by "[]". Here is a somewhat contrived example which attempts
to append the elements of the first argument to the second:
def probably_incorrect_function(a, b=[]):
for element in a:
b.append(element)
return b
Try to call this function more than once:
probably_incorrect_function((1, 2))
probably_incorrect_function((1, 2))
The
first time the function returns "[1, 2]" (as intended), wile the second
time it returns "[1, 2, 1, 2]". This is because variable "b" always
refers to the same object which was created when the python interpreter
processed the function definition.
The correct implementation looks like this:
def correct_function(a, b=None):
if b is None:
b = []
for element in a:
b.append(element)
return b
Note that the default parameter value is now immutable, and evaluation of the "b is None" statement is very fast: it tests for identity rather than equality.
List Comprehensions
Instead
of using the "for" statement to loop over the contents of a list or
tuple, simple operations on lists can be performed in one line of code
using syntax constructs called "list comprehensions". Here is an
example:
x = (0.0, 1.0, 2.0, 3.0, 4.0, 5.0)
y = [z*z for z in x]
print "z is", z, " y is", y
The single line "y = [z*z for z in x]" is equivalent to (but more concise than)
y = []
for z in x:
y.append(z*z)
One can put a conditional statement into a list comprehension. Example:
y = [z*z for z in x if z > 1]
print "y is", y
In this example the list comprehension statement is equivalent to
y = []
for z in x:
if z > 1:
y.append(z*z)
List
comprehensions can be conveniently used with functions which take
sequences as their arguments. Here, for example, is a sum of squares of
integers from 0 to 9, skipping 5:
sum([z*z for z in xrange(10) if z != 5])
One
can build really complex comprehensions with multiple "for" and "if"
statements inside. Try to avoid those -- they are more difficult to
understand than explicit loops.