程式扎記: [Quick Python] 8. Control flow

標籤

2012年2月3日 星期五

[Quick Python] 8. Control flow

Preface : 
Python provides a complete set of control-flow elements, with loops and conditionals. This chapter examines each in detail. This chapter covers : 
* Repeating code with a while loop
* Making decisions: the if-elif-else statement
* Iterating over a list with a for loop
* Using list and dictionary comprehensions
* Delimiting statements and blocks with indentation
* Evaluating Boolean values and expressions

The while loop : 
You’ve come across the basic while loop several times already. The full while loop looks like this : 
  1. while condition:  
  2.     body  
  3. else:  
  4.     post-code  
condition is an expression that evaluates to a true or false value. As long as it’s True, the body will be executed repeatedly. If it evaluates to False, the while loop will execute the post-code section and then terminate. If the condition starts out by being false, the body won’t be executed at all—just the post-code section. The body andpost-code are each sequences of one or more Python statements that are separated by newlines and are at the same level of indentation. The Python interpreter uses this level to delimit them. No other delimiters, such as braces or brackets, are necessary. 

Note that the else part of the while loop is optional and not often used. That’s because as long as there is no break in the body, this loop : 
  1. while condition:  
  2.     body  
  3. else:  
  4.     post-code  
and this loop : 
  1. while condition:  
  2.     body  
  3. post-code  
do the same things—and the second is simpler to understand. I probably wouldn’t have mentioned the else clause except that if you haven’t learned about it by now, you may have found it confusing if you found this syntax in another person’s code. Also, it’s useful in some situations. 

- The break and continue statements 
The two special statements break and continue can be used in the body of a while loop. If break is executed, it immediately terminates the while loop, and not even thepost-code (if there is an else clausewill be executed. If continue is executed, it causes the remainder of the body to be skipped over; the condition is evaluated again, and the loop proceeds as normal. 

The if-elif-else statement : 
The most general form of the if-then-else construct in Python is : 
  1. if condition1:  
  2.     body1  
  3. elif condition2:  
  4.     body2  
  5. elif condition3:  
  6.     body3  
  7. .  
  8. .  
  9. .  
  10. elif condition(n-1):  
  11.     body(n-1)  
  12. else:  
  13.     body(n)  
It says: if condition1 is true, execute body1; otherwise, if condition2 is true, execute body2; otherwise … and so on, until it either finds a condition that evaluates to True or hits the else clause, in which case it executes body(n). As for the while loop, the body sections are again sequences of one or more Python statements that are separated by newlines and are at the same level of indentation. 

Of course, you don’t need all that luggage for every conditional. You can leave out the elif parts, or the else part, or both. If a conditional can’t find any body to execute (no conditions evaluate to True, and there is no else part), it does nothing. 

The body after the if statement is required. But you can use the pass statement here (as you can anywhere in Python where a statement is required). The passstatement serves as a placeholder where a statement is needed, but it performs no action : 
  1. if x < 5:  
  2.     pass  
  3. else:  
  4.     x = 5  
There is no case (or switch) statement in Python. 

The for loop : 
A for loop in Python is different from for loops in some other languages. The traditional pattern is to increment and test a variable on each iteration, which is what C for loops usually do. In Python, a for loop iterates over the values returned by any iterable object—that is, any object that can yield a sequence of values. For example, a for loop can iterate over every element in a list, a tuple, or a string. But an iterable object can also be a special function called range() or a special type of function called agenerator. This can be quite powerful. The general form is : 
  1. for item in sequence:  
  2.     body  
  3. else:  
  4.     post-code  
body will be executed once for each element of sequence. variable is set to be the first element of sequence, and body is executed; then, variable is set to be the second element of sequence, and body is executed; and so on, for each remaining element of the sequence

The else part is optional. As with the else part of a while loop, it’s rarely used. break and continue do the same thing in a for loop as in a while loop. Below small loop prints out the reciprocal of each number in x : 
  1. x = [1.02.03.0]  
  2. for n in x:  
  3.     print(1 / n)  
- The range function 
Sometimes you need to loop with explicit indices (to use the position at which values occur in a list). You can use the range() command together with the len() command on lists to generate a sequence of indices for use by the for loop. This code prints out all the positions in a list where it finds negative numbers : 
 

Given a number nrange(n) returns a sequence 0, 1, 2, …, n–2, n–1. So, passing it the length of a list (found using len()) produces a sequence of the indices for that list’s elements. The range() function doesn’t build a Python list of integers—it just appears to. Instead, it creates a range object that produces integers on demand. This is useful when you’re using explicit loops to iterate over really large lists. Instead of building a list with 10 million elements in it, for example, which would take up quite a bit of memory, you can use range(10000000), which takes up only a small amount of memory and generates a sequence of integers from 0 up to 10000000 as needed by the for loop. 

CONTROLLING STARTING AND STEPPING VALUES WITH RANGE 
You can use two variants on the range() function to gain more control over the sequence it produces. If you use range with two numeric arguments, the first argument is the starting number for the resulting sequence and the second number is the number the resulting sequence goes up to (but doesn’t include). Here are a few examples :
>>> list(range(3, 7)) # The generated list doesn't contain 7!
[3, 4, 5, 6]
>>> list(range(2, 10))
[2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(5, 3))
[]
list() is used only to force the items range would generate to appear as a list. It’s not normally used in actual code. 

This still doesn’t allow you to count backward, which is why the value of range(5, 3) is an empty list. To count backward, or to count by any amount other than 1, you need to use the optional third argument to range(), which gives a step value by which counting proceeds : 
>>> list(range(0, 10, 2))
[0, 2, 4, 6, 8]
>>> list(range(5, 0, -1))
[5, 4, 3, 2, 1]

(Sequences returned by range always include the starting value given as an argument to range and never include the ending value given as an argument

- Using break and continue in for loops 
The two special statements break and continue can also be used in the body of a for loop. If break is executed, it immediately terminates the for loop, and not even thepost-code (if there is an else clause) will be executed. If continue is executed in a for loop, it causes the remainder of the body to be skipped over, and the loop proceeds as normal with the next item. 

- The for loop and tuple unpacking 
You can use tuple unpacking to make some for loops cleaner. The following code takes a list of two-element tuples and calculates the value of the sum of the products of the two numbers in each tuple (a moderately common mathematical operation in some fields) : 
  1. somelist = [(12), (37), (95)]  
  2. result = 0  
  3. for t in somelist:  
  4.     result = result + (t[0] * t[1])  
Here’s the same thing, but cleaner : 
  1. somelist = [(12), (37), (95)]  
  2. result = 0  
  3. for x, y in somelist:  
  4.     result = result + (x * y)  
We use a tuple x, y immediately after the for keyword, instead of the usual single variable. On each iteration of the for loop, x contains element 0 of the current tuple from list, and y contains element 1 of the current tuple from list. Using a tuple in this manner is a convenience of Python, and doing this indicates to Python that each element of the list is expected to be a tuple of appropriate size to unpack into the variable names mentioned in the tuple after the for

- The enumerate function 
You can combine tuple unpacking with the enumerate() function to loop over both the items and their index. This is similar to using [url=http://docs.python.org/library/functions.html#range]range()[/u] but has the advantage that the code is clearer and easier to understand. Like the previous example, the following code prints out all the positions in a list where it finds negative numbers : 
  1. x = [13, -749, -54]  
  2. for i, n in enumerate(x):  # (1)  
  3.     if n < 0:  # (2)  
  4.         print("Found a negative number at index ", i)   # (3)  
The enumerate() function returns tuples of (index, item). You can access the item without the index (2). The index is also available (3). 

- The zip function 
Sometimes it’s useful to combine two or more iterables before looping over them. The zip() function will take the corresponding elements from one or more iterables and combine them into tuples until it reaches the end of the shortest iterable : 
>>> x = [1, 2, 3, 4] # x has 4 elements
>>> y = ['a', 'b', 'c'] # y has 3 elements
>>> z = zip(x, y)
>>> list(z)
[(1, 'a'), (2, 'b'), (3, 'c')] # z has only min(len(x), len(y)) elements

List and dictionary comprehensions : 
The pattern of using a for loop to iterate through a list, modify or select individual elements, and create a new list or dictionary is very common. Such loops often look a lot like the following : 
 

This sort of situation is so common that Python has a special shortcut for such operations, called a comprehension. You can think of a list or dictionary comprehension as a one-line for loop that creates a new list or dictionary from another list. The pattern of a list comprehension is as follows : 
new_list = [expression for variable in old_list if expression]

And a dictionary comprehension looks like this : 
new_dict = {expression:expression for variable in list if expression}

In both cases, the heart of the expression is similar to the beginning of a for loop—for variable in list—with some expression using that variable to create a new key or value and an optional conditional expression using the value of the variable to select whether it’s included in the new list or dictionary. For example, the following code does exactly the same thing as the previous code but is a list comprehension : 
>>> x = [1, 2, 3, 4]
>>> x_squared = [item * item for item in x]
>>> x_squared
[1, 4, 9, 16]

You can even use if statements to select items from the list : 
>>> x = [1, 2, 3, 4]
>>> x_squared = [item * item for item in x if item > 2]
>>> x_squared
[9, 16]

Dictionary comprehensions are similar, but you need to supply both a key and a value. If we want to do something similar to the previous example but have the number be the key and the number’s square be the value in a dictionary, we can use a dictionary comprehension, like so : 
>>> x = [1, 2, 3, 4]
>>> x_squared_dict = {item: item * item for item in x}
>>> x_squared_dict
{1: 1, 2: 4, 3: 9, 4: 16}

Statements, blocks, and indentation : 
Because the control flow constructs we encountered in this chapter are the first to make use of blocks and indentation, this is a good time to revisit the subject. 

Python uses the indentation of the statements to determine the delimitation of the different blocks (or bodies) of the control-flow constructs. A block consists of one or more statements, which are usually separated by newlines. Examples of Python statements are the assignment statement, function calls, the print() function, the placeholder pass statement, and the del statement. The control-flow constructs (if-elif-else, while, and for loops) are compound statements : 
  1. compound statement clause:  
  2.     block  
  3. compound statement clause:  
  4.     block  
A compound statement contains one or more clauses that are each followed by indented blocks. Compound statements can appear in blocks just like any other statements. When they do, they create nested blocks. You may also encounter a couple of special cases. Multiple statements may be placed on the same line if they are separated by semicolons. A block containing a single line may be placed on the same line after the semicolon of a clause of a compound statement : 
>>> x = 1; y = 0; z = 0
>>> if x > 0: y = 1; z = 10
... else: y = -1
...
>>> print(x, y, z)
1 1 10

Improperly indented code will result in an exception being raised. You may encounter two forms of this. The first is : 
 

We indented a line that should not have been indented. In the basic mode, the carat (^) indicates the spot where the problem occurred. In the IDLE Python Shell (see figure 8.1), the invalid indent is highlighted. The same message would occur if we didn’t indent where necessary (that is, the first line after a compound statement clause). 
 

One situation where this can occur can be confusing. If you’re using an editor that displays tabs in four-space increments (or the Windows interactive mode, which indents the first tab only four spaces from the prompt) and indent one line with four spaces and then the next line with a tab, the two lines may appear to be at the same level of indentation. But you’ll receive this exception because Python maps the tab to eight spaces. The best way to avoid this problem is to use only spaces in Python code. If you must use tabs for indentation, or if you’re dealing with code that uses tabs, be sure never to mix them with spaces. 

The second form of exception will occur if you indent a statement in a block less than the legal amount : 
 
(Here, the line containing z = 2 isn’t lined up properly under the line containing y = 2.) 

Python will allow you to indent any amount and won’t complain regardless of how much you vary it as long as you’re consistent within a single block. Please don’t take improper advantage of this. The recommended standard is to use four spaces for each level of indentation. 

Before leaving indentation, I’ll cover breaking up statements across multiple lines. This of course is necessary more often as the level of indentation increases. You can explicitly break up a line using the backslash character. You can also implicitly break any statement between tokens when within a set of (), {}, or [] delimiters (that is, when typing a set of values in a list, a tuple, or a dictionary, or a set of arguments in a function call, or any expression within a set of brackets). You can indent the continuation line of a statement to any level you desire : 
 

You can break a string with a \ as well. But any indentation tabs or spaces will become part of the string, and the line must end with the \. To avoid this, you can use the fact that any set of string literals separated by whitespace is automatically concatenated : 
 

Boolean values and expressions : 
The previous examples of control flow use conditional tests in a fairly obvious manner but never really explain what constitutes true or false in Python or what expressions can be used where a conditional test is needed. This section describes these aspects of Python. 

Python has a Boolean object type that can be set to either True or False. Any expression with a Boolean operation will return True or False

- Most Python objects can be used as Booleans 
In addition, Python is similar to C with respect to Boolean values, in that C uses the integer 0 to mean false and any other integer to mean true. Python generalizes this idea; 0 or empty values are False, and any other values are True. In practical terms, this means the following : 
* The numbers 0, 0.0, and 0+0j are all False; any other number is True.
* The empty string "" is False; any other string is True.
* The empty list [] is False; any other list is True.
* The empty dictionary {} is False; any other dictionary is True.
* The empty set set() is False; any other set is True.
* The special Python value None is always False.

There are some Python data structures we haven’t looked at yet, but generally the same rule applies. If the data structure is empty or 0, it’s taken to mean false in a Boolean context; otherwise it’s taken to mean true. Some objects, such as file objects and code objects, don’t have a sensible definition of a 0 or empty element, and these objects shouldn’t be used in a Boolean context. 

- Comparison and Boolean operators 
You can compare objects using normal operators: <<=>>=, and so forth. == is the equality test operator, and either != or <> may be used as the “not equal to” test. There are also in and not in operators to test membership in sequences (lists, tuples, strings, and dictionaries) as well as is and is not operators to test whether two objects are the same. 

Expressions that return a Boolean value may be combined into more complex expressions using the andor, and not operators. This code snippet checks to see if a variable is within a certain range : 
  1. if 0 < x and x < 10:  
  2.     ...  
Python offers a nice shorthand for this particular type of compound statement; you can write it as you would in a math paper : 
  1. if 0 < x < 10:  
  2.     ...  
Various rules of precedence apply; when in doubt, you can use parentheses to make sure Python interprets an expression the way you want it to. This is probably a good idea for complex expressions, regardless of whether it’s necessary, because it makes it clear to future maintainers of the code exactly what’s happening. 

The and and or operators return objects. The and operator returns either the first false object (that an expression evaluates to) or the last object. Similarly, the oroperator returns either the first true object or the last object. As with many other languages, evaluation stops as soon as a true expression is found for the or operator or as soon as a false expression is found for the and operator : 
>>> [2] and [3, 4] and op will return last True
[3, 4]
>>> [] and 5 and op will return first False
[]
>>> [2] or [3, 4] # or op will return first True
[2]
>>> [] or 5
5
>>> [] or 0 or op will return last False
0

The == and != operators test to see if their operands contains the same values. They are used in most situations. The is and is not operators test to see if their operands are the same object : 
>>> x = [0]
>>> y = [x, 1]
>>> x is y[0] # They refer to the same object
True
>>> x = [0] # x has benn assigned to a different object
>>> x is y[0]
False
>>> x == y[0]
True

Writing a simple program to analyze a text file : 
To give you a better sense of how a Python program works, let’s look a small sample that roughly replicates the UNIX wc utility and reports the number of lines, words, and characters in a file. The sample in listing 8.1 is deliberately written to be clear to programmers new to Python and as simple as possible : 
- word_count.py :
  1. """ Read s file and returns the number of lines, words and  
  2.     characters - similar to the UNIX wc utility  
  3. """  
  4. import sys  
  5.   
  6. gb_word_count=0  
  7. gb_char_count=0  
  8. gb_line_count=0  
  9. index = 0  
  10. file_list = []  
  11. for f in sys.argv:  
  12.     word_count=0  
  13.     char_count=0  
  14.     if index > 0:  
  15.         print("Process file={0}...".format(f))  
  16.         file_list.append(f)  
  17.         infile = open(f,'r')  
  18.         lines = infile.read().split("\n")  
  19.         count = len(lines)  
  20.         for line in lines:  
  21.             words = line.split()  
  22.             word_count += len(words)  
  23.             char_count += len(line)  
  24.         gb_word_count += word_count  
  25.         gb_char_count += char_count  
  26.         gb_line_count += count  
  27.         print("\tFile({0}) has {1} lines, {2} words, {3} characters".format(  
  28.               f, count, word_count, char_count))  
  29.         infile.close()  
  30.     index+=1  
  31.   
  32.   
  33. print("All of them has {0} lines, {1} words and {2} characters!".format(  
  34.       gb_line_count, gb_word_count, gb_char_count))  

To test, you can create a file "test.txt" which containing below content and pass its' file path as argument to "word_count.py" : 
- test.txt : 
Python provides a complete set of control flow elements,
including while and for loops, and conditionals.
Python uses the level of indentation to group blocks
of code with control elements.

The execution result may look like below : 
 

Supplement : 
[Python 學習筆記] 起步走 : 陳述句 (if 陳述句) 
[Python 學習筆記] 起步走 : 陳述句 (for 與 while 陳述句) 
[Python 學習筆記] 起步走 : 陳述句 (for 包含式 - Comprehension)

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!