Reallife instructions often ask you questions before they tell you what to do. For example,
Computer programs ask questions of their data in a similar way. For example, the computer program that dispenses money from a cash machine contains a command like this:
IF the person's account balance is less than the amount of money that the person wishes to withdraw, THEN display an error message and return the person's bank cardHere, a question (called a ``test'' or a ``condition,'' prefixed by IF) triggers a command: When the answer is ``yes'' (called ''True''), then the the bank card is returned.
A MarioBrothers computer game contains complex commands like this one, which examine the state of the game, instant by instant, and update the game accordingly:
Move Mario one step, in the direction the human indicated with the joystick. IF Mario has stepped on a gold piece: THEN give Mario one more energy point. ELSE IF Mario has stepped on a bomb: THEN subtract one energy point. IF Mario's energy points are now zero: THEN print "Game Over". ELSE IF Mario has ... ...The example shows how one question can lead to other questions. This style of asking questions is the secret to how a computer program exhibits humanlike intelligence. To master the style, we must understand the linguistics, the foundations, and the design applications of the conditional (''if'') command.
The Python language has a prebuilt ``miniprogram'' (a function), called sqrt, that lives in a file (module) named math. We use it like this:
$ python >>> import math >>> math.sqrt(3) 1.7320508075688772(The import math command makes the module available, so that we can use its sqrt miniprogram.)
If we try to use sqrt with a negative number, it is a disaster:
>>> math.sqrt(3) Traceback (most recent call last): File "We wish to prevent such an error.", line 1, in ? ValueError: math domain error >>>
Here is a small Python script, SQ.py, that asks the human to type an integer for calculating its square root:
============================================= # SQ.py computes a square root of a nonnegative int. # input: a nonnegative int # output: a nonnegative square root import math num = int(raw_input("Type an int: ")) if num >= 0 : answer = math.sqrt(num) print answer raw_input("\npress Enter to finish") ===============================================The conditional command asks if the input, num, has a value greaterorequal to zero. If the answer is yes (True), then a square root is printed.
Try the script:
$python SQ.py Type an int: 3 1.73205080757 press Enter to finishThe answer prints  the program asked the question (the condition or test), calculated an answer of True, and calculated the square root.
Try the script with a negative input:
$python SQ.py Type an int: 3 press Enter to finishNo answer prints, because the test computed to False  the command to compute the square root with sqrt was skipped.
The philosophy behind the conditional command is this:
In the example, we cannot guarantee that the input number is nonnegative, so we ask and only then do we undertake the planned computation.
In the previous chapter, the above example might have been handled like this, using the assert command:
==================================================== # SQassert uses an assert command to ensure that sqrt works properly. import math num = int(raw_input("Type an int: ")) assert num >= 0 answer = math.sqrt(num) print answer raw_input("\npress Enter to finish") ==================================================The above program behaves like this:
$ python SQassert.py Type an int: 3 Traceback (most recent call last): File "SQassert.py", line 7, in ? assert num >= 0 AssertionErrorThe assert command prevented sqrt from malfunctioning, but at the cost of quitting the program prematurely and printing a technical error message. In contrast, the conditional command seen earlier guides the computer around the trouble spot to a proper conclusion.
You will see the difference if you save both SQ.py and SQassert.py and start each by doubleclicking on their respective icons. Supply a negative number as input to each; what happens? Now, say that you are writing a computer program that controls a cash machine  if there is bad input to the machine, do you want the machine to avoid the bad computation or do you want it to stop functioning on the spot?
if CONDITION : COMMANDswhere CONDITION is an expression that computes to True or False. A condition typically has the form, EXPRESSSION OP EXPRESSION, where a comparison operation, OP, can be
>, <, >=, <=, ==, !=Read >= as ``greater than or equals to''; read == as ``equals'' (this is not an assignment!); and read != as ``not equals''. An example is:
if (num  1) >= 0 : print "the num is positive"You must have at least one space between the keyword, if, and the CONDITION, but spaces are not required between the CONDITION and the colon.
The COMMANDs are one or more commands, one command per line. The commands must be indented, say, by 4 spaces or one tab. Here is an example:
if num == 0 : print num, print "is zero" print "Have a nice day"The two print commands are executed when the condition computes to True, and after the conditional command finishes, the last command, print "Have a nice day", is always executed  the indentation indicates this. (Notice that num == 0 asks whether the value of num equals 0.)
Inconsistent indentation within an ifcommand causes an error  this example is wrong
if num == 0 : print num, print "is zero" print "Have a nice day"because the indentation of the two commands inside the ifcommand is inconsistent.
if CONDITION : COMMANDsthe computer does these steps:
Here is an example. In his text, Dawson uses a builtin Python function, randrange, to generate a random number:
import Random # the builtin function lives in module/file Random ... die_roll = Random.randrange(1,7)This generates a random integer in the range of 1 to 6 (!) and assigns it to die_roll.
Perhaps we build a program that plays a dice game, and the program asks the user whether she wishes to roll a die one more time. We write this:
import Random ... total_score = ... # the player's score so far question = "Do you want to throw the die again (type y or n)? " answer = raw_input(question) if answer == "y" : die_roll = Random.randrange(1,7) total_score = total_score + die_roll print "Your score is", total_scoreThe ifcommand compares the user's answer to the string, "y"; if the comparison computes to True, then a die is ``thrown'' and added to the total score. If the comparison computes to False, no action is taken. Finally, the score is printed.
This little example illustrates a standard design pattern: When a program needs more information to continue its computation, it asks the user a question and uses an ifcommand to analyze the answer.
====================================== import math num = int(raw_input("Type an int: ")) if num >= 0 : # assert: num >= 0 (that is, num is nonnegative and has a square root) answer = math.sqrt(num) # assert: answer is a number print answer raw_input("Press Enter to finish") =======================================The test of the conditional command generates new knowledge  when the test computes to True, then the knowledge gained ensures that the squareroot calculation will proceed correctly. When the test computes to False, the knowledge gained prevents an erroneous attempt to compute square root.
We see this clearly when we draw the program as a circuitlike flowchart:
At the end of the flowchart, we do not have knowledge whether num is nonnegative or not  this is because the two ``paths'' in the ``circuit'' join together.
Here is a second example, where we must ask whether the input integer is even valued before we print an answer:
================================================ answer = 1 # assert : answer is (an) odd (number) num = int(raw_input("Type an int:") if (num % 2) == 0 : # is num an even number (has a remainder of zero) ? # assert: num is even (if the computer reaches this position, # it is because the test computed to True) answer = num + 1 # assert: num is even and answer = num + 1 # implies: answer is odd # assert: answer is odd print answer raw_input("Press Enter to finish") ====================================================The knowledge generated by the test ensures that answer is always oddvalued at the end of the program:
The technical explanation is that, at the conclusion of the ifcommand, we assert
assert: (num is even and answer is odd) or (answer is odd)because one of the two outcomes surely must occur. From this fact we conclude, regardless of the outcome, that answer is odd.
The Python method, isdigit, helps us check if a string contains all digits. For example,
>>> s = "123" >>> print s.isdigit() True >>> t = "ab2" >>> print t.isdigit() FalseHere is how we use this operation to guard against badly typed, nonnumeric input:
====================================== import math text = raw_input("Type an int: ")) if text.isdigit() : # assert: text holds only digits num = int(text) if num >= 0 : # assert: num >= 0 print math.sqrt(num) raw_input("\npress Enter to finish") =======================================We placed a conditional command inside a conditional command to ask whether the input is numeric and if it is, then nonnegative.
=========================================================== # SQ2.py computes a square root of a nonnegative int. # input: a nonnegative int # output: a nonnegative square root or an error message import math num = int(raw_input("Type an int: ")) if num >= 0 : # assert: num is nonnegative; it has a square root: print math.sqrt(num) else : # assert: num is < 0 (negative) print "Sorry  cannot compute square root of a negative number" print # a blank line raw_input("press Enter to finish") =============================================================Try it:
$ python sq2.py Type an int: 3 Sorry  cannot compute square root of a negative numberThe experiment shows that when the condition is False, the command labelled by else executes.
The syntax format of the ifelsecommand is
if CONDITION : TRUE_COMMANDs else : FALSE_COMMANDswhere TRUE_COMMANDs and FALSE_COMMANDs are sequences of one or more commands. The two groups of commands must be indented by the same amount of spaces/tabs.
Some people like to visualize an IFELSE conditional command as a ``fork in the road'' in their program; they draw a picture (a flowchart) of it like this:
 v CONDITION? / \ True / \ False v v TRUE_COMMANDS FALSE_COMMANDS \ / \_ _/  vThe two paths fork apart and join together again, displaying the two possible ``flows of control.'' The picture also inspires this terminology: We call the TRUE_COMMANDs the thenarm and FALSE_COMMANDs the elsearm. (Many programming languages write a conditional as if CONDITION then COMMANDs else COMMANDsS; the keyword, then, is omitted from Python's conditional but the terminology is preserved.)
When you design a conditional command, you might find it helpful to draw the flowchart first and write the Python coding second. Here is another example, which ensures that we can print a square root for every integer (because we use the integer's absolute value):
============================================ # SQalways.py computes a square root for the absolute value of an int. # asssumed input: an integer # guaranteed output: the sqaure root for the input's absolute value. import math num = int(raw_input("Type an int: ")) if num >= 0 : absolute_value = num else : absolute_value = num print math.sqrt(absolute_value) raw_input("\npress Enter to finish") ===============================================
Perhaps a friendlier version of the above program is one that asks its user if the square root of a negative number is desired. Nested conditional commands can do this:
============================================ # SQ3.py computes a square root of an int. # If the int is negative, the user has the option of computing # the square root of the number's negation # input: an int # output: a nonnegative square root or nothing import math num = int(raw_input("Type an int: ")) if num >= 0 : # assert: num is nonnegative print math.sqrt(num) else : # assert: num is < 0 (negative) response = raw_input( "Do you want the square root of the negation? (y or n)? " ) if response == "y" : print math.sqrt(num) else : print "OK  bye!" print # a blank line raw_input("press Enter to finish") ====================================================
========================================== # SQalways.py computes a square root for the absolute value of an int. # asssumed input: an integer # guaranteed output: the sqaure root for the input's absolute value. num = int(raw_input("Type an int: ")) if num >= 0 : # assert: num >= 0 absolute_value = num # assert: num >= 0 and absolute_value = num # implies: absolute_value >= 0 else : # assert: not(num >= 0) # implies: num < 0 absolute_value = num # assert: num < 0 and absolute_value = num # implies absolute_value >= 0 #! assert in both cases: absolute_value >= 0 print math.sqrt(absolute_value) # assert: a number is printed raw_input("\npress Enter to finish") ===========================================
Both the then and elsearms exploit the answer that the test might calculate to achieve the same fact  that absolute_value >= 0. Because this fact holds in all cases, we can assert it when the conditional command finishes, at the position labelled by #!. This ensures that a square root is always correctly computed.
Here is a flowchart drawing of the above example:
The pattern of knowledge production for an ifelse command looks like this:
if TEST : # assert: TEST THEN_COMMANDS # assert: GOAL else : # assert: not TEST ELSE_COMMANDs # assert: GOAL # assert, in both cases: GOALThat is,
Here is the same idea, drawn flowchartstyle:
 v TEST ? / \ True / \ False v v assert TEST assert not TEST THEN_COMMANDS ELSE_COMMANDS assert GOAL assert GOAL \ / \_ _/  assert GOAL  v
===================== num = int( raw_input("Type an int: ")) # Line 1 # 2 if num < 0 : # 3 print "is negative" # 4 else : # 5 print "is nonnegative" # 6 # 7 ============================When the program starts at Line 1, the instruction counter (``i.c.'') holds 1, and there are no variables in the program's namespace. Here is a picture of the computer's configuration:
From now on, we show just the instruction counter and the namespace, since the program stays the same. The instructor counter and the namespace will be typed like this:
i.c.: 1 namespace: (empty)The first command asks the user to enter an integer; say that 42 is typed. This causes 42 to be extracted and saved in the newly creadted variable, num:
i.c.: 3 namespace: num: 42At Line 3, the expression, num < 0 is computed. Of course, the comparison, 42 < 0 computes to False. This makes the instruction counter reset to the first instruction of the conditional's elsearm:
i.c.: 6 namespace: num: 42At instruction 6, the message, is nonnegative, prints, and the instruction counter is set to the command following the conditional:
i.c.: 7 namespace: num: 42
Now, let's repeat the execution with a different test case. We again start with the instruction counter at 1 and an empty namespace:
i.c.: 1 namespace: (empty)Say that the user types 3 as the input. At the start of the conditional, Line 3, we have this configuration:
i.c.: 3 namespace: num: 3At Line 3, the expression, num < 0 is computed, and the comparison, 3 < 0 computes to True. This makes the instruction counter reset to the first instruction of the conditional's thenarm:
i.c.: 4 namespace: num:3At instruction 4, the message, is negative, prints, and the instruction counter is again set to the command following the conditional:
i.c.: 7 namespace: num: 3
Here is the example:
=================================================== import math num = int(raw_input("Type an int: ")) if (num == 0) or (num > 0) : # num is zero or positive ? print math.sqrt(num) =====================================================
The or operation is called (logical disjunction): if either (or both!) of its argments computes to True, then the result of the question is True as well.
More precisely stated, the test calculates to True if the first argument, num == 0 , computes to True. If num == 0 computes to False, then the second argument is computedif num > 0 computes to True then the test computes to True. If both phrases compute to False, then so does the test.
We have used and (logical conjunction) in program assertions; we can also use it in tests. Here is a Python coding of the example where we limit the program to computing square roots in the range of 0 to 10:
=================================================== import math num = int(raw_input("Type an int: ")) if (num >= 0) and (num <= 10) : # num in range of 0 to 10? print math.sqrt(num) else : print "Sorry  the number must fall between 0 and 10" =====================================================Both expressions of the conjunction must be True for the test to compute to True: The first argument, num >= 0, is computed; if it is False, the elsearm is immediately taken; if it is True, then num <= 10 is computed, and its result determines which arm executes.
We can also use not (negation), which ``flips'' the answer of a test:
if not(num < 0) : #again, is num nonnegative ? print math.sqrt(num)Of course, this test asks the same question as does num >= 0.
Table 5 summarizes the relational operations and their semantics.
TABLE 5: logical operations===========================================
Operator  Semantics 
E1 and E2 
conjunction (``and''):

E1 or E2 
disjunction (``or''):

not E 
negation(``not''):

ENDTABLE================================================================Finally, we can write arbitrarily complex expressions with the relational operations. Here is an example that checks whether a number is either 99 or a nonnegative, evenvalued int:
n = int(raw_input("Type an int: ")) if (n == 99) or (not(n < 0) and (n%2 == 0)) : print "it's either 99 or it's a nonnegative, even int"Notice how extra parentheses are used to indicate how the comparisons should be grouped. (The not operator is always applied to the first argument that follows it, here n < 0.) It is safe, however, to omit parentheses when a sequence of the same relational operation is used, e.g.,
if (n == 2) or (n == 3) or (n == 5) or (n > 99) : ...There is no need to group the arguments for the multiple or operations, since the computation will proceed left to right in any case.
# Withdrawal # does subtraction # assumed inputs: arg1  a nonnegative int # arg2  an int lessorequalto than arg1 # guaranteed output: arg1  arg2 int arg1 = int(raw_input("Type balance: ")) int arg2 = int(raw_input("Type withdrawal amount: ")) if arg1 > 0 and arg1 >= arg2 : print "New balance is", (arg1  arg2) else : print "Transaction voided"what results are returned for these argument pairs: 3 and 2 ? 2 and 3 ? 3 and 3 ? 4 and 5 ? 4 and 5 ?
Say that the Withdrawal program is embedded into a cash dispenser. Improve the program so that it behaves ``correctly'' for all five test cases just mentioned.
# SmallPrime # says whether its argument is a prime number less than 10 # assumed input: an int # guaranteed output: a message stating whether the int is a prime # number less than 10 (NOTE: the only primes less than 10 are # 2, 3, 5, and 7.)
Sometimes one wants to use knowledge where there are multiple (more than two) cases to consider. For example, we wish to determine whether an input number is either negative, or zero, or positive. Here is Python's way of asking multiple questions within a single conditional:
================================= # PrintSign # prints the sign of a number # asssumed input: an integer # guaranteed output: a message as to whether or not the # number is negative, zero, or positive num = int( raw_input("Type an int: ")) if num < 0 : print "is negative" elif num == 0 : print "is zero" else : # assert: num > 0 must be true print "is positive" raw_input("\n\npress Enter to finish") ======================================Try it:
$ python PrintSign.py Type an int: 16 is positiveAgain:
$ python PrintSign.py Type an int: 0 is zeroAnd again:
$ python PrintSign.py Type an int: 99 is negativeRead the elif as ``elseif'', which means that if the previous condition was False, then we ask another question.
The compound conditional looks like this:
if TEST1 : COMMANDs1 # do this if TEST1 is True elif TEST2 : # ask this when TEST1 is False COMMANDs2 # do this if TEST2 is True elif TEST3 : # ask this when TEST1 and TEST2 are False COMMANDs3 # do this if TEST3 is True ... # ... continue this way through all the TESTs.... else : COMMANDsLAST # do this when _all_ TESTs are FalseThe semantics of the command is described by the comments inserted into the syntax description: The conditions are computed one by one, until a condition computes to True. At that point, the corresponding set of commands are executed. If all the conditions compute to False, the commands labelled by else are computed.
Here is a flowchart drawing of the compound conditional:
 v False False TEST1? > TEST2? > TEST3? ... +     True  True True False v v v v COMMANDS1 COMMANDS2 COMMANDS3 COMMANDS_LAST     +++ ... +  VFinally, it is crucial to understand the knowledge that is generated by asking the series of questions/tests; it looks like this:
if TEST1 : # assert: TEST1 COMMANDs1 elif TEST2 : # assert: not TEST1 and TEST2 COMMANDs2 elif TEST3 : # assert: not TEST1 and not TEST2 and TEST3 COMMANDs3 ... # ... continue this way through all the TESTs.... else : # assert not TEST1 and not TEST2 and not TEST3 ... COMMANDsLASTThe more questions that are asked, the more knowledge that is gained for the benefit of the COMMANDs that are finally selected to compute.
Here is a second example, where a nested conditional asks about the possible cases that arrive within an input request. The example is a program that can convert temperatures from Celsius to Fahrenheit and from Fahrenheit to Celsius, for example:
$python ConvertTemps.py Is the temperature Celsius or Fahrenheit (C or F)? C Please type the temperature: 22 22 degrees Celsius is 71.6 degrees FahrenheitThere are three possible cases for the first input from the human:
read the base of the temperature read the temperature  V base = "C" ? > base == "F" ? > otherwise:    V V V convert the convert the print error message Celsius temp Fahrenheit  to Fahrenheit temp to Celsius     +++  VHere is the program, which uses a compound conditional to code the flowchart:
FIGURE========================================================== # ConvertTemps converts between Celsius and Fahrenheit temperatures. # assumed input: a base ("C" for Celsius and "F" for Fahrenheit) # the temperature to convert, an int # guaranteed output: the temperature converted to the other base base = raw_input("Is the temperature Celsius or Fahrenheit (C or F)? ") temp = int(raw_input("Please type the temperature: ")) if base == "C" : # assert: base == "C" output_temp = ((9.0 / 5.0) * temp ) + 32 print temp, "degrees Celsius is ", output_temp, "degrees Fahrenheit" elif base == "F" : # assert: base == "F" output_temp = ((5 * temp)  160) / 9.0 print temp, "degrees Fahrenheit is ", output_temp, "degrees Celsius" else: # assert: base != "C" and base != "F" print "error in the temperature base; no conversion performed" ENDFIGURE======================================================The ``goal'' of each of the three plans of actions is to compute the form of conversion desired by the human. In the third case, when the base is erroneous, no conversion is appropriate.
# WithdrawCash # subtracts its second argument from its first, provided that # the result is nonnegative # assumed inputs: arg1  the first input, must be a nonnegative float # arg2  the second argument, a float # guaranteed output: (arg1  arg2), if arg1 >= arg2;
# Divide # does integer division on its arguments # assumed inputs: arg1  the dividend, a float # arg2  the divisor, a float that is not zero # guaranteed output: arg1 / arg2
Flowcharts are a better language for solutions to questionproblems, because they are a nice mix of graphics and English. Following is an example.
In practice, two main uses of conditionals are
We desire a program, TwelveHourClock, which converts a time from a twentyfour hour clock into the corresponding value on a twelvehour clock. Here is a typical behavior:
Type time (24hour clock): 16:35 The time is 4:35 p.m.and here is another:
Type time (24hour clock): 00:05 The time is 12:05 a.m.The program must be intelligent enough to understand that the hours 0 to 11 are ``a.m.'' hours and that 12 to 23 are ''p.m.'' Also, the ''p.m.'' hours are rewritten as 12, 1, 2, ..., 11, and the very first ``a.m.'' hour is also 12.
Take a moment to practice these conversions yourself: How do you convert 16:35 to 4:35 p.m.? 00:05 to 12:05 a.m.? 09:00 to 9:00 a.m.? What is the pattern/strategy you use? Can you write it down? It's time to try:
Our first attempt at the algorithm might go like this:
hours == 0 ? hours > 12 ? hours between 1 and 12 ?    V V V new_hours = 12 new_hours = hours  12 new_hours = hours    +++  assert: new_hours is correct hours in 12hourclock time  VThe flowchart helps us organize our thinking: The hours value might be any of 0, 1, 2, ..., 12, 13, ..., 23, and our grouping of the 24 values into 3 cases helps us plan the strategies for computation.
You are welcome to write little flowcharts for Steps 3 and 6, which also have some interest. Figure 7 shows the program that we code from the flowcharts.
FIGURE 7: program that uses conditionals to convert time================== # TwelveHourClock # converts a 24hourclock time into a 12hourclock time. # assumed inputs: hour  the hour time, in the range 0..23 # minute  the minutes time, in the range 0..59 # guaranteed output: the 12hourclock time # Step 1: input = raw_input("Type time (24hour clock): ") hours_and_minutes = input.split(":") # split breaks a string into pieces hour_text = hours_and_minutes[0] # get the first piece (see Chap. 4) minute_text = hours_and_minutes[1] # get the second piece (see Chap. 4) if hour_text.isdigit() and minute_text.isdigit() : # assert: we can convert the inputs into ints hours = int(hour_text) minutes = int(minute_text) # Step 2: if hours < 0 or hours > 23 or minutes < 0 or minutes > 59 : print "Error: hours or minutes are out of range" else : # assert: hours in 0..23 and minutes in 0..59 # Step 3: if hours < 12 : ampm = "a.m." else : ampm = "p.m." # assert in both cases: a.m./p.m. distinction correct # Step 4: if hours > 12 : new_hours = hours  12 elif hours == 0 : new_hours = 12 else : new_hours = hours # assert in all cases: hours correctly computed # Step 5: minutes is already correctly computed # Step 6: print import string # find string module to use here: print str(new_hours) + ":" + string.zfill(minutes, 2) + ampm else : print "Error: input are not numbers" raw_input("\npress Enter to finish") ENDFIGURE============================================================Within Step 1, there is a new Python trick, which will be completely understood in a couple of chapters:
Since the input to the program is a string, which is in fact two numbers separated by a colon, we must ``split'' the string into its two pieces. Python has a special method, named split, that we use:
input = raw_input(...) hours_and_minutes = input.split(":")The second command splits the string, input into two pieces, discarding the colon in the middle, and saving the two stringpieces in variable hours_and_minutes
We extract the two pieces (strings) like this:
hour_text = hours_and_minutes[0] minute_text = hours_and_minutes[1](the details are given in Chapter 5), and we make the two pieces into integers:
hours = int(hour_text) minutes = int(minute_text)The split is trick is useful for splitting apart hoursminutes, dollarscents, wordwordword, etc.
The remainder of the program makes careful use of the questions we ask of the values of the hours and minutes. When the answer is printed, we use a helper function, string.zfill, which ensures that we print the minutes amounts as a twodigit integer.
# TranslateGrade # converts a numerical score to a letter grade. # assumed input: score  a numerical score that must be in the range 0..100 # guaranteed output: a letter grade based on this scale: # 100..90 = "A"; 89..80 = "B"; 79..70 = "C"; 69..60 = "D"; 59..0 = "F"
Nickels = 1 Pennies = 2
1 quarter 2 dimes 1 penny
The program in Figure 7 is a bit hard to read  we must remember a long history of tests to know when a command in the program's middle might execute  so we might ``unnest'' the conditionals in the Figure with a boolean variable, which is a variable that holds as its value either True or False.
Boolean variables are good for remembering a history of knowledge gained from conditionals' tests.
In the timeconversion program, we must ask several questions about the inputs before we proceed to time conversion. If any of the answers indicate that the inputs are improper, we must skip the time conversion. See Figure 8, which rewrites the timeconversion program with a boolean variable, OK, that remembers whether the inputs are acceptable.
FIGURE 8: unnesting conditional statements============================= # MakeChange2 # calculates change in coins for a dollars, cents amount # assumed input: dollars  a nonnegative integer # cents  an integer between 0 and 99 # guaranteed output: the coins for the amount # Step 1: input = raw_input("Type time (24hour clock): ") hours_and_minutes = input.split(":") # split breaks a string into pieces hour_text = hours_and_minutes[0] # get the first piece (see Chap. 4) minute_text = hours_and_minutes[1] # get the second piece (see Chap. 4) OK = True # asserts whether the inputs are acceptable for time conversion # Step 2: if (not hour_text.isdigit()) or not(minute_text.isdigit()) : print "Error: input are not numbers" OK = False if OK : # assert: we can convert the inputs into ints hours = int(hour_text) minutes = int(minute_text) if hours < 0 or hours > 23 or minutes < 0 or minutes > 59 : print "Error in inputs" OK = False if OK : # assert: hours in 0..23 and minutes in 0..59, so OK to convert time! # Step 3: if hours < 12 : ampm = "a.m." else : ampm = "p.m." # assert in both cases: a.m./p.m. distinction correct # Step 4: if hours > 12 : new_hours = hours  12 elif hours == 0 : new_hours = 12 else : new_hours = hours # assert in all cases: hours correctly computed # Step 5: minutes is already correctly computed # Step 6: print import string # find string module to use here: print str(new_hours) + ":" + string.zfill(minutes, 2) + ampm raw_input("\npress Enter to finish") ENDFIGURE=============================================================
The boolean variable is set to False if any test uncovers a bad input. The subsequent commands consult OK to determine whether the computation may proceed. By using OK, we need not write so many nested conditional commands.
================================================ import sys # finds the module with ``system'' functions, like exit # Step 1: input = raw_input("Type time (24hour clock): ") hours_and_minutes = input.split(":") # split breaks a string into pieces hour_text = hours_and_minutes[0] # get the first piece (see Chap. 4) minute_text = hours_and_minutes[1] # get the second piece (see Chap. 4) # Step 2: if (not hour_text.isdigit()) or not(minute_text.isdigit()) : print "Error: input are not numbers" sys.exit() # STOP DEAD, HERE! # assert: program is still computing, so we can convert the inputs into ints hours = int(hour_text) minutes = int(minute_text) if hours < 0 or hours > 23 or minutes < 0 or minutes > 59 : print "Error in inputs" sys.exit() # assert: program is still computing, # so hours in 0..23 and minutes in 0..59, so OK to convert time! # Step 3: if hours < 12 : ampm = "a.m." else : ampm = "p.m." # assert in both cases: a.m./p.m. distinction correct # Step 4: if hours > 12 : new_hours = hours  12 elif hours == 0 : new_hours = 12 else : new_hours = hours # assert in all cases: hours correctly computed # Step 5: minutes is already correctly computed # Step 6: print import string # find string module to use here: print str(new_hours) + ":" + string.zfill(minutes, 2) + ampm raw_input("\npress Enter to finish") =====================================================The sys.exit command is actually a separate, small program (``function'') that is prewritten and saved in a package named sys. We must import sys to start the exit program.
Use the exit command with caution  it stops your program and the Python interpreter, and nothing can happen after it executes. (Demo the above program by saving it and doubleclicking on its icon  what happens when you provide bad input?)
Truly, sys.exit is a ``last resort'' when planning a computation strategy  use it when you are truly lost and cannot recover from a disastrous situation. (The above use of sys.exit is overkill.)
# ConvertToSeconds # converts an hours, minutes amount into the equivalent time in seconds. # assumed inputs: hours  the hours, a nonnegative integer # minutes  the minutes, an integer in the range 0..59 # guaranteed output: the time in seconds
In the previous chapter, we noted that we should test a program to see if it behaves the way we want. This means we should try the program with the testcase behaviors we started from when we designed the program. (Testing the testcase behaviors is sometimes called ``black box'' testing, because the program is treated like it is resting inside a black box and we do not look inside it to see what happens  we only watch what the program produces as output.)
But programs that contain conditional statements should undergo a second round of testing where, for every ifcommand in the program, there is a test case that makes the command's thenarm execute and there is another test case that makes the command's elsearm execute. This ensures that every command inside the program is tested at least once! (Testing the conditions this way is sometimes called ``white box'' or ''clear box'' testing, because we look inside the program to see what happens with the test cases.)
For example, consider this structure of conditionals, which is checking the inputs to a banking machine:
if dollars < 0 : ... else : ... if cents < 0 : ... elif cents > 99 : ... else ...This asks questions about the input values of dollars and cents. We must test this program with at least the following test cases:
As we noted in the previous chapter, it is easy to start a Python program  doubleclick on its icon. But if the program contains an error, then the error message will flash onto the display and disappear almost immediately!
It is better to test a new Python program by starting it within a command window:
This starts your program and makes the program show its work in the opened commandprompt window. If there is an error, the error message will appear in the window.
This technique is important, because it is easy to forget to insert colons and uniform indentation into conditional commands. When the Python interpreter notes that you have forgotten to do this, it will issue an error message, and you will certainly want to read it!
The new COMMAND is the CONDIIONAL, which can have these forms of syntax:
if CONDITION : COMMANDs
if CONDITION : COMMANDs1 else : COMMANDs2
if CONDITION1 : COMMANDs1 elif CONDITION2 : COMMANDs2 elif CONDITION3: COMMANDs3 ... else : COMMANDsLAST
A CONDITION is an expression that computes to a boolean (TrueFalse) value. CONDITIONs can be built in these ways:
Boolean variables can be used to simplify the structure of a program that must ask many questions to choose its course of action. The style looks like this:
ok = True # remembers if the computation has proceeded correctly so far ... # compute if BAD_CONDITION : ok = False if ok : # if no BAD_CONDITION so far, proceed: ... # compute if BAD_CONDITION : ok = False else : ... # compute if ok : # if no BAD_CONDITION so far, proceed: ... # computeIf a program must be stopped immediately, use the exit command:
import sys sys.exit()
The following special functions were introduced in this chapter:
import Random # the builtin function lives in module/file Random ... die_roll = Random.randrange(1,7)This generates a random integer in the range of 1 to 6 (!) and assigns it to die_roll.
import math math.sqrt(NUMBER)computes the square root of a nonnegative number
import string string.zfill(NUMBER,PLACES)computes a string that represents NUMBER expanded to PLACESmany digits. (For example, print string.zfile(15,5) prints 00015.)
items = STRING.split(SEPARATOR) first = item[0] second = item[1]splits a STRING into two pieces, at the position in the string where the character, SEPARATOR, appears. The pieces are extracted by the second and third commands. The complete explanation is given in Chapter 5.