8.5 pass Statement
pass 声明

One Python statement not found in C is the pass statement. Because Python does not use curly braces to delimit blocks of code, there are places where code
is syntactically required. We do not have the equivalent empty braces or single semicolon the way C does to indicate “do nothing.” If you use a Python state- ment that expects a sub-block of code or suite, and one is not present, you will get a syntax error condition. For this reason, we have pass, a statement that does absolutely nothing—it is a true NOP, to steal the “No OPeration” assembly code jargon. Style- and development-wise, pass is also useful in places where your code will eventually go, but has not been written yet (in stubs, for example):

def foo_func():

if user_choice == 'do_calc':
pass else:


This code structure is helpful during the development or debugging stages
because you want the structure to be there while the code is being created, but you do not want it to interfere with the other parts of the code that have been completed already. In places where you want nothing to execute, pass is a good tool to have in the box.
Another popular place is with exception handling, which we will take a look at in Chapter 10; this is where you can track an error if it occurs, but take no action if it is not fatal (you just want to keep a record of the event or perform an operation under the covers if an error occurs).

8.6 else Statement . . . Take Two
else 声明

In C (as well as in most other languages), you will not find an else statement outside the realm of conditional statements, yet Python bucks the trend again by offering these in while and for loops. How do they work? When used with loops, an else clause will be executed only if a loop finishes to comple-
tion, meaning they were not abandoned by break.
One popular example of else usage in a while statement is in finding the
largest factor of a number. We have implemented a function that performs
this task, using the else statement with our while loop. The showMaxFac- tor() function in Example 8.1 (maxFact.py) utilizes the else statement
as part of a while loop.


Example 8.1 while-else Loop Example (maxFact.py)

This program displays the largest factors for numbers between 10 and 20. If the number is prime, the script will indicate that as well.

The loop beginning on line 3 in showMaxFactor() counts down from half the amount (starts checking if two divides the number, which would give the largest factor). The loop decrements each time (line 10) through until a divisor
is found (lines 6–9). If a divisor has not been found by the time the loop decre- ments to 1, then the original number must be prime. The else clause on lines
11–12 takes care of this case. The main part of the program on lines 14 –15 fires
off the requests to showMaxFactor() with the numeric argument. Running our program results in the following output:

largest factor of 10 is 5
11 is prime
largest factor of 12 is 6
13 is prime
largest factor of 14 is 7 largest factor of 15 is 5 largest factor of 16 is 8
17 is prime
largest factor of 18 is 9
19 is prime
largest factor of 20 is 10
Likewise, a for loop can have a post-processing else. It operates exactly the same way as for a while loop. As long as the for loop exits normally (not via break), the else clause will be executed. We saw such an example in Section 8.5.3.
Table 8.1 summarizes with which conditional or looping statements auxil- iary statements can be used.


Table 8.1 Auxiliary Statements to Loops and Conditionals

Loops and Conditionals Auxiliary Statements if while for elif •
else • • • break • • continue • •

• • •

a. pass is valid anywhere a suite (single or multiple statements) is required (also includes elif,
else, class, def, try, except, finally).


8.11 Iterators and the iter()
8.11.1 What Are Iterators?

Iterators were added to Python in version 2.2 to give sequence-like objects
a sequence-like interface. We formally introduced sequences back in Chapter 6. They are just data structures that you can “iterate” over by using their index starting at 0 and continuing till the final item of the sequence. Because you can do this “counting,” iterating over sequences is trivial. Iteration support
in Python works seamlessly with sequences but now also allows program- mers to iterate through non-sequence types, including user-defined objects.

Iterators come in handy when you are iterating over something that is not
a sequence but exhibits behavior that makes it seem like a sequence, for example, keys of a dictionary, lines of a file, etc. When you use loops to iterate over an object item, you will not be able to easily tell whether it is an iterator or a sequence. The best part is that you do not have to care because Python makes it seem like a sequence.

8.11.2 Why Iterators?

The defining PEP (234) cites that iterators:

• Provide an extensible iterator interface.
• Bring performance enhancements to list iteration.
• Allow for big performance improvements in dictionary iteration.
• Allow for the creation of a true iteration interface as opposed to overriding methods originally meant for random element access.
• Be backward-compatible with all existing user-defined classes and extension objects that emulate sequences and mappings.
• Result in more concise and readable code that iterates over non-sequence collections (mappings and files, for instance).


8.11.3 How Do You Iterate?

Basically, instead of an index to count sequentially, an iterator is any item that has a next() method. When the next item is desired, either you or a looping mechanism like for will call the iterators next()method to get

the next value. Once the items have been exhausted, a StopIteration exception is raised, not to indicate an error, but to let folks know that we are done.
Iterators do have some restrictions, however. For example, you cannot move backward, go back to the beginning, or copy an iterator. If you want to iterate over the same objects again (or simultaneously), you have to create another iterator object. It isn’t all that bad, however, as there are various tools
to help you with using iterators.
There is a reversed() built-in function that returns an iterator that traverses an iterable in reverse order. The enumerate() BIF also returns an iterator. Two new BIFs, any() and all(), made their debut in Python 2.5— they will return True if any or all items traversed across an iterator have a Boolean True value, respectively. We saw earlier in the chapter how you can use it in a for loop to iterate over both the index and the item of an iterable. There is also an entire module called itertools that contains various itera- tors you may find useful.

8.11.4 Using Iterators with . . .


As mentioned before, iterating through Python sequence types is as expected:

>>> myTuple = (123, 'xyz', 45.67)
>>> i = iter(myTuple)
>>> i.next()
>>> i.next()
>>> i.next()
>>> i.next()
Traceback (most recent call last): File "", line 1, in ?

If this had been an actual program, we would have enclosed the code inside a try-except block. Sequences now automatically produce their own iterators, so a for loop:

for i in seq:

under the covers now really behaves like this:

fetch = iter(seq)
while True:
i = fetch.next()
except StopIteration:

However, your code does not need to change because the for loop itself calls the iterator’s next() method (as well as monitors for StopIteration).



Dictionaries and files are two other Python data types that received the itera- tion makeover. A dictionary’s iterator traverses its keys. The idiom for eachKey in myDict.keys() can be shortened to for eachKey in myDict as shown here:

>>> legends = { ('Poe', 'author'): (1809, 1849, 1976),
... ('Gaudi', 'architect'): (1852, 1906, 1987),
... ('Freud', 'psychoanalyst'): (1856, 1939, 1990)
... }
>>> for eachLegend in legends:
... print 'Name: %s\tOccupation: %s' % eachLegend
... print ' Birth: %s\tDeath: %s\tAlbum: %s\n' \
... % legends[eachLegend]
Name: Freud Occupation: psychoanalyst
Birth: 1856 Death: 1939 Album: 1990

Name: Poe Occupation: author
Birth: 1809 Death: 1849 Album: 1976

Name: Gaudi Occupation: architect
Birth: 1852 Death: 1906 Album: 1987

In addition, three new built-in dictionary methods have been introduced
to define the iteration: myDict.iterkeys()(iterate through the keys), myDict.itervalues() (iterate through the values), and myDict.iter- items() (iterate through key/value pairs). Note that the in operator has been modified to check a dictionary’s keys. This means the Boolean expression myDict.has_key(anyKey) can be simplified as anyKey in myDict.


File objects produce an iterator that calls the readline() method. Thus, they loop through all lines of a text file, allowing the programmer to replace essentially for eachLine in myFile.readlines() with the more simplistic for eachLine in myFile:

>>> myFile = open('config-win.txt')
>>> for eachLine in myFile:
... print eachLine, # comma suppresses extra \n
font-name: courier new font-size: 10
>>> myFile.close()
文件对象会产生一个地代器调用readline()方法。因此,循环会遍历文本文件所有的行,并允许程序员用更加简单的for eachLine in myFile替代
for eachLine in myFile.readlines():

>>> myFile = open('config-win.txt')
>>> for eachLine in myFile:
... print eachLine, # comma suppresses extra \n
font-name: courier new font-size: 10
>>> myFile.close()


8.11.5 Mutable Objects and Iterators

Remember that interfering with mutable objects while you are iterating them is not a good idea. This was a problem before iterators appeared. One popular example of this is to loop through a list and remove items from it if certain criteria are met (or not):

for eachURL in allURLs:
if not eachURL.startswith('http://'):
allURLs.remove(eachURL) # YIKES!!
All sequences are immutable except lists, so the danger occurs only there. A sequence’s iterator only keeps track of the Nth element you are on, so if you change elements around during iteration, those updates will be reflected as you traverse through the items. If you run out, then StopIteration will be raised. When iterating through keys of a dictionary, you must not modify the dictio- nary. Using a dictionary’s keys() method is okay because keys() returns a list that is independent of the dictionary. But iterators are tied much more inti- mately with the actual object and will not let us play that game anymore:

>>> myDict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> for eachKey in myDict:
... print eachKey, myDict[eachKey]
... del myDict[eachKey]
... a 1
Traceback (most recent call last): File "", line 1, in ?
RuntimeError: dictionary changed size during iteration
This will help prevent buggy code. For full details on iterators, see PEP 234.


8.11.6 How to Create an Iterator

You can take an item and call iter() on it to turn it into an iterator. Its syn- tax is one of the following:

iter(func, sentinel )
If you call iter() with one object, it will check if it is just a sequence, for which the solution is simple: It will just iterate through it by (integer) index from 0 to the end. Another way to create an iterator is with a class. As we will see in Chapter 13, a class that implements the __iter__() and next() methods can be used as an iterator.
If you call iter() with two arguments, it will repeatedly call func to obtain the next value of iteration until that value is equal to sentinel.


8.12 List Comprehensions
List 结构

List comprehensions (or “list comps” for short) come to us from the func- tional programming language Haskell. They are an extremely valuable, sim- ple, and flexible utility tool that helps us create lists on the fly. They were added to Python in version 2.0.
Up ahead in Functions (Chapter 11), we will be discussing long-time Python functional programming features like lambda, map(), and filter(). They have been around in Python for quite a while, but with list comprehensions, they have simplified their use to only requiring a list comp instead. map() is a function that applies an operation to list members, and filter() filters out list members based on a conditional expression. Finally, lambda allows you to create one-line function objects on the fly. It is not important that you learn them now, but you will see examples of them in this section because we are discussing the merits of list comps. Let us take a look at the simpler list com- prehension syntax first:

[expr for iter_var in iterable]
The core of this statement is the for loop, which iterates over each item of iterable. The prefixed expr is applied for each member of the sequence, and the resulting values comprise the list that the expression yields. The iter- ation variable need not be part of the expression.
Here is a sneak preview of some code from Chapter 11. It has a lambda
function that squares the members of a sequence:

>>> map(lambda x: x ** 2, range(6))
[0, 1, 4, 9, 16, 25]


We can replace this code with the following list comprehension statement:

>>> [x ** 2 for x in range(6)]
[0, 1, 4, 9, 16, 25]

In the new statement, only one function call (range()) is made (as opposed
to three—range(), map(), and the lambda function). You may also use
parentheses around the expression if [(x ** 2) for x in range(6)] is
easier for you to read. This syntax for list comprehensions can be a substitute for
and is more efficient than using the map() built-in function along with lambda.
List comprehensions also support an extended syntax with the if

[expr for iter_var in iterable if cond_expr]

This syntax will filter or “capture” sequence members only if they meet the condition provided for in the cond_expr conditional expression during iteration.
Recall the following odd() function below, which determines whether a numeric argument is odd or even (returning 1 for odd numbers and 0 for even numbers):

def odd(n):
return n % 2

We were able to take the core operation from this function, and use it with
filter() and lambda to obtain the set of odd numbers from a sequence:

>>> seq = [11, 10, 9, 9, 10, 10, 9, 8, 23, 9, 7, 18, 12, 11, 12]
>>> filter(lambda x: x % 2, seq)
[11, 9, 9, 9, 23, 9, 7, 11]

As in the previous example, we can bypass the use of filter() and
lambda to obtain the desired set of numbers with list comprehensions:

>>> [x for x in seq if x % 2]
[11, 9, 9, 9, 23, 9, 7, 11]

Let us end this section with a few more practical examples.

Matrix Example

Do you want to iterate through a matrix of three rows and five columns? It is as easy as:

>>> [(x+1,y+1) for x in range(3) for y in range(5)]
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2), (2,
3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]

Disk File Example

Now let us say we have the following data file and want to count the total number of non-whitespace characters in the file hhga.txt:

And the Lord spake, saying, "First shalt thou take out the Holy Pin. Then shalt thou count to three, no more, no less. Three shall be the number thou shalt count, and the number of the counting shall be three. Four shalt thou not count, nei- ther count thou two, excepting that thou then proceed to three. Five is right out. Once the number three, being the third number, be reached, then lobbest thou thy Holy Hand Grenade of Antioch towards thy foe, who, being naughty in My sight, shall snuff it."
We know that we can iterate through each line with for line in data, but more than that, we can also go and split each line up into words, and we can sum up the number of words to get a total like this:

>>> f = open('hhga.txt', 'r')

>>> len([word for line in f for word in line.split()])
Let us get a quick total file size:

import os
>>> os.stat('hhga.txt').st_size
Assuming that there is at least one whitespace character in the file, we know that there are fewer than 499 non-whitespace characters in the file. We can sum up the length of each word to arrive at our total:

>>> f.seek(0)
>>> sum([len(word) for line in f for word in line.split()])
Note we have to rewind back to the beginning of the file each time through because the iterator exhausts it. But wow, a non-obfuscated one- liner now does something that used to take many lines of code to accomplish! As you can see, list comps support multiple nested for loops and more than one if clause. The full syntax can be found in the official documenta- tion. You can also read more about list comprehensions in PEP 202.

8.13 Generator Expressions

Generator expressions extend naturally from list comprehensions (“list comps”). When list comps came into being in Python 2.0, they revolutionized

the language by giving users an extremely flexible and expressive way to designate the contents of a list on a single line. Ask any long-time Python user what new features have changed the way they program Python, and list comps should be near the top of the list.
Another significant feature that was added to Python in version 2.2 was the generator. A generator is a specialized function that allows you to return a value and “pause” the execution of that code and resume it at a later time. We will discuss generators in Chapter 11.
The one weakness of list comps is that all of the data have to be made available in order to create the entire list. This can have negative conse- quences if an iterator with a large dataset is involved. Generator expressions resolve this issue by combining the syntax and flexibility of list comps with the power of generators.
Introduced in Python 2.4, generator expressions are similar to list compre- hensions in that the basic syntax is nearly identical; however, instead of build- ing a list with values, they return a generator that “yields” after processing each item. Because of this, generator expressions are much more memory efficient by performing “lazy evaluation.” Take a look at how similar they appear to list comps:


[expr for iter_var in iterable if cond_expr]


(expr for iter_var in iterable if cond_expr)

Generator expressions do not make list comps obsolete. They are just a more memory-friendly construct, and on top of that, are a great use case of generators. We now present a set of generator expression examples, includ- ing a long-winded one at the end showing you how Python code has changed over the years.

Disk File Example

In the previous section on list comprehensions, we took a look at finding the total number of non-whitespace characters in a text file. In the final snippet
of code, we showed you how to perform that in one line of code using a list comprehension. If that file became unwieldy due to size, it would become fairly unfriendly memory-wise because we would have to put together a very long list of word lengths.


Instead of creating that large list, we can use a generator expression to per-
form the summing. Instead of building up this long list, it will calculate individual lengths and feed it to the sum() function, which takes not just lists but also iterables like generator expressions. We can then shorten our example above
to be even more optimal (code- and execution-wise):

>>> sum(len(word) for line in data for word in line.split())
All we did was remove the enclosing list comprehension square
brackets: Two bytes shorter and it saves memory . . . very environmen-
tally friendly!

Cross-Product Pairs Example

Generator expressions are like list comprehensions in that they are lazy, which is their main benefit. They are also great ways of dealing with other lists and generators, like rows and cols here:

rows = [1, 2, 3, 17]

def cols(): # example of simple generator
yield 56 yield 2 yield 1

We do not need to create a new list. We can piece together things on the fly. Let us create a generator expression for rows and cols:

x_product_pairs = ((i, j) for i in rows for j in cols())

Now we can loop through x_product_pairs, and it will loop through
rows and cols lazily:

>>> for pair in x_product_pairs:
... print pair
(1, 56)
(1, 2)
(1, 1)
(2, 56)
(2, 2)
(2, 1)
(3, 56)
(3, 2)
(3, 1)

(17, 56)
(17, 2)
(17, 1)

Refactoring Example

Let us look at some evolutionary code via an example that finds the longest line in a file. In the old days, the following was acceptable for reading a file:

f = open('/etc/motd', 'r')
longest = 0
while True:
linelen = len(f.readline().strip())
if not linelen: break if linelen > longest: longest = linelen
return longest

Actually, this is not that old. If it were really old Python code, the Boolean constant True would be the integer one, and instead of using the string strip() method, you would be using the string module:

import string

Since that time, we realized that we could release the (file) resource sooner if we read all the lines at once. If this was a log file used by many pro- cesses, then it behooves us not to hold onto a (write) file handle for an extended period of time. Yes, our example is for read, but you get the idea. So the preferred way of reading in lines from a file changed slightly to reflect this preference:

f = open('/etc/motd', 'r')
longest = 0
allLines = f.readlines()
for line in allLines:
linelen = len(line.strip())
if linelen > longest:
longest = linelen
return longest

List comps allow us to simplify our code a little bit more and give us the ability to do more processing before we get our set of lines. In the next

snippet, in addition to reading in the lines from the file, we call the string
strip() method immediately instead of waiting until later.

f = open('/etc/motd', 'r')
longest = 0
allLines = [x.strip() for x in f.readlines()]
for line in allLines:
linelen = len(line)
if linelen > longest:
longest = linelen
return longest

Still, both examples above have a problem when dealing with a large file as readlines() reads in all its lines. When iterators came around, and files became their own iterators, readlines() no longer needed to be called. While we are at it, why can’t we just make our data set the set of line lengths
(instead of lines)? That way, we can use the max() built-in function to get the longest string length:

f = open('/etc/motd', 'r')
allLineLens = [len(x.strip()) for x in f]
return max(allLineLens)

The only problem here is that even though you are iterating over f line by line, the list comprehension itself needs all lines of the file read into memory
in order to generate the list. Let us simplify our code even more: we will replace the list comp with a generator expression and move it inside the call
to max() so that all of the complexity is on a single line:

f = open('/etc/motd', 'r')
longest = max(len(x.strip()) for x in f)
return longest

One more refactoring, which we are not as much fans of, is dropping the file mode (defaulting to read) and letting Python clean up the open file. It is not as bad as if it were a file open for write, however, but it does work:

return max(len(x.strip()) for x in open('/etc/motd'))

We have come a long way, baby. Note that even a one-liner is not obfuscated enough in Python to make it difficult to read. Generator expressions were added in Python 2.4, and you can read more about them
in PEP 289.

8.14 Related Modules

Iterators were introduced in Python 2.2, and the itertools module was added in the next release (2.3) to aid developers who had discovered how useful iterators were but wanted some helper tools to aid in their development. The interesting thing is that if you read the documentation for the various utilities in itertools, you will discover generators. So there is a relationship between iterators and generators. You can read more about this relationship
in Chapter 11, “Functions.”

8.15 Exercises

8–1. Conditionals. Study the following code:
# statement A
if x > 0:
# statement B

elif x < 0:
# statement C

# statement D

# statement E

(a) Which of the statements above (A, B, C, D, E) will be exe- cuted if x < 0?
(b) Which of the statements above will be executed if x == 0?
(c) Which of the statements above will be executed if x > 0?
8–2. Loops. Write a program to have the user input three (3) numbers: (f)rom, (t)o, and (i)ncrement. Count from f to t in increments of i, inclusive of f and t. For example, if the input
is f == 2, t == 26, and i == 4, the program would output: 2, 6,
10, 14, 18, 22, 26.
8–3. range(). What argument(s) could we give to the range()
built-in function if we wanted the following lists to be generated?
(a) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(b) [3, 6, 9, 12, 15, 18]
(c) [-20, 200, 420, 640, 860]


8–4. Prime Numbers. We presented some code in this chapter to
determine a number’s largest factor or if it is prime. Turn this code into a Boolean function called isprime() such that
the input is a single value, and the result returned is True if
the number is prime and False otherwise.
8–5. Factors. Write a function called getfactors() that takes a
single integer as an argument and returns a list of all its fac-
tors, including 1 and itself.
8–6. Prime Factorization. Take your solutions for isprime() and getfactors() in the previous problems and create a function that takes an integer as input and returns a list of its prime factors. This process, known as prime factorization, should output a list of
factors such that if multiplied together, they will result in the origi- nal number. Note that there could be repeats in the list. So if you gave an input of 20, the output would be [2, 2, 5].
8–7. Perfect Numbers. A perfect number is one whose factors (except itself ) sum to itself. For example, the factors of 6 are 1, 2, 3, and 6. Since 1  2  3 is 6, it (6) is considered a perfect number. Write a function called isperfect() which takes a single integer input and outputs 1 if the number is perfect and 0 otherwise.
8–8. Factorial. The factorial of a number is defined as the product of all values from one to that number. A shorthand for N fac-

torial is N! where N! == factorial(N) == 1
  
2 3
. . .
 


N. So 4! == 1
  
2 3
4. Write a routine such that
given N, the value N! is returned.
8–9. Fibonacci Numbers. The Fibonacci number sequence is 1, 1,
2, 3, 5, 8, 13, 21, etc. In other words, the next value of the sequence is the sum of the previous two values in the sequence. Write a routine that, given N, displays the value of the Nth Fibonacci number. For example, the first Fibonacci number
is 1, the 6th is 8, and so on.
8–10. Text Processing. Determine the total number of vowels, conso- nants, and words (separated by spaces) in a text sentence.
Ignore special cases for vowels and consonants such as “h,” “y,”
“qu,” etc. Extra credit: create code to handle those special case.
8–11. Text Processing. Write a program to ask the user to input a list of names, in the format “Last Name, First Name,” i.e.,
last name, comma, first name. Write a function that manages the input so that when/if the user types the names in the wrong order, i.e., “First Name Last Name,” the error is cor- rected, and the user is notified. This function should also
keep track of the number of input mistakes. When the user

is done, sort the list, and display the sorted names in “Last
Name, First Name” order.

EXAMPLE input and output (you don’t have to do it this way exactly):
% nametrack.py
Enter total number of names: 5

Please enter name 0: Smith, Joe
Please enter name 1: Mary Wong
>> Wrong format... should be Last, First.
>> You have done this 1 time(s) already. Fixing input... Please enter name 2: Hamilton, Gerald
Please enter name 3: Royce, Linda
Please enter name 4: Winston Salem
>> Wrong format... should be Last, First.
>> You have done this 2 time(s) already. Fixing input...

The sorted list (by last name) is: Hamilton, Gerald
Royce, Linda Salem, Winston Smith, Joe Wong, Mary

8–12. (Integer) Bit Operators. Write a program that takes begin and end values and prints out a decimal, binary, octal, hexadeci- mal chart like the one shown below. If any of the characters are printable ASCII characters, then print those, too. If none is, you may omit the ASCII column header.

Enter begin value: 9
Enter end value: 18
9 01001 11 9
10 01010 12 a
11 01011 13 b
12 01100 14 c
13 01101 15 d
14 01110 16 e
15 01111 17 f
16 10000 20 10
17 10001 21 11
18 10010 22 12


Enter begin value: 26
Enter end value: 41
26 011010 32 1a
27 011011 33 1b
28 011100 34 1c
29 011101 35 1d
30 011110 36 1e
31 011111 37 1f
32 100000 40 20
33 100001 41 21 !
34 100010 42 22 "
35 100011 43 23 #
36 100100 44 24 $
37 100101 45 25 %
38 100110 46 26 &
39 100111 47 27 '
40 101000 50 28 (
41 101001 51 29 )

8–13. Performance. In Section 8.5.2, we examined two basic ways
of iterating over a sequence: (1) by sequence item, and (2) via sequence index. We pointed out at the end that the latter
does not perform as well over the long haul (on my system here, a test suite shows performance is nearly twice as bad
[83% worse]). Why do you think that is?