We’ll see all these syntax rules in action when we tour Python’s specific compound statements in the next few chapters, but they work the same everywhere in the Python language. To get started, let’s work through a brief, realistic example that demonstrates the way that statement syntax and statement nesting come together in practice, and introduces a few statements along the way.
Suppose you’re asked to write a Python program that interacts with a user in a console window. Maybe you’re accepting inputs to send to a database, or reading numbers to be used in a calculation. Regardless of the purpose, you need to code a loop that reads one or more inputs from a user typing on a keyboard, and prints back a result for each. In other words, you need to write a classic read/evaluate/print loop program.
In Python, typical boilerplate code for such an interactive loop might look like this:
while True:
reply = input('Enter text:')
if reply == 'stop': break
print(reply.upper())
This code makes use of a few new ideas and some we’ve already seen:
The code leverages the Python while loop, Python’s most general looping statement. We’ll study the while statement in more detail later, but in short, it consists of the word while, followed by an expression that is interpreted as a true or false result, followed by a nested block of code that is repeated while the test at the top is true (the word True here is considered always true).
The input built-in function we met earlier in the book is used here for general console input—it prints its optional argument string as a prompt and returns the user’s typed reply as a string. Use raw_input in 2.X instead, per the upcoming note.
A single-line if statement that makes use of the special rule for nested blocks also appears here: the body of the if appears on the header line after the colon instead of being indented on a new line underneath it. This would work either way, but as it’s coded, we’ve saved an extra line.
Finally, the Python break statement is used to exit the loop immediately—it simply jumps out of the loop statement altogether, and the program continues after the loop. Without this exit statement, the while would loop forever, as its test is always true.
In effect, this combination of statements essentially means “read a line from the user and print it in uppercase until the user enters the word ‘stop.’” There are other ways to code such a loop, but the form used here is very common in Python code.
Notice that all three lines nested under the while header line are indented the same amount—because they line up vertically in a column this way, they are the block of code that is associated with the while test and repeated. Either the end of the source file or a lesser-indented statement will suffice to terminate the loop body block.
When this code is run, either interactively or as a script file, here is the sort of interaction we get—all of the code for this example is in interact.py in the book’s examples package:
Enter text:spamSPAM Enter text:4242 Enter text:stop
Version skew note: This example is coded for Python 3.X. If you are working in Python 2.X, the code works the same, but you must use raw_input instead of input in all of this chapter’s examples, and you can omit the outer parentheses in print statements (though they don’t hurt). In fact, if you study the interact.py file in the examples package, you’ll see that it does this automatically—to support 2.X compatibility, it resets input if the running Python’s major version is 2 (“input” winds up running raw_input):
import sys if sys.version[0] == '2': input = raw_input # 2.X compatible
In 3.X, raw_input was renamed input, and print is a built-in function instead of a statement (more on prints in the next chapter). Python 2.X has an input too, but it tries to evaluate the input string as though it were Python code, which probably won’t work in this context; eval(input()) can yield the same effect 3.X.
Our script works, but now suppose that instead of converting a text string to uppercase, we want to do some math with numeric input—squaring it, for example, perhaps in some misguided effort of an age-input program to tease its users. We might try statements like these to achieve the desired effect:
>>>reply = '20'>>>reply ** 2...error text omitted...TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
This won’t quite work in our script, though, because (as discussed in the prior part of the book) Python won’t convert object types in expressions unless they are all numeric, and input from a user is always returned to our script as a string. We cannot raise a string of digits to a power unless we convert it manually to an integer:
>>> int(reply) ** 2
400
Armed with this information, we can now recode our loop to perform the necessary math. Type the following in a file to test it:
while True:
reply = input('Enter text:')
if reply == 'stop': break
print(int(reply) ** 2)
print('Bye')
This script uses a single-line if statement to exit on “stop” as before, but it also converts inputs to perform the required math. This version also adds an exit message at the bottom. Because the print statement in the last line is not indented as much as the nested block of code, it is not considered part of the loop body and will run only once, after the loop is exited:
Enter text:24 Enter text:401600 Enter text:stopBye
Usage note: From this point on I’ll assume that this code is stored in and run from a script file, via command line, IDLE menu option, or any of the other file launching techniques we met in Chapter 3. Again, it’s named interact.py in the book’s examples. If you are entering this code interactively, though, be sure to include a blank line (i.e., press Enter twice) before the final print statement, to terminate the loop. This implies that you also can’t cut and paste the code in its entirety into an interactive prompt: an extra blank line is required interactively, but not in script files. The final print doesn’t quite make sense in interactive mode, though—you’ll have to code it after interacting with the loop!
So far so good, but notice what happens when the input is invalid:
Enter text:xxx...error text omitted...ValueError: invalid literal for int() with base 10: 'xxx'
The built-in int function raises an exception here in the face of a mistake. If we want our script to be robust, we can check the string’s content ahead of time with the string object’s isdigit method:
>>>S = '123'>>>T = 'xxx'>>>S.isdigit(), T.isdigit()(True, False)
This also gives us an excuse to further nest the statements in our example. The following new version of our interactive script uses a full-blown if statement to work around the exception on errors:
while True:
reply = input('Enter text:')
if reply == 'stop':
break
elif not reply.isdigit():
print('Bad!' * 8)
else:
print(int(reply) ** 2)
print('Bye')
We’ll study the if statement in more detail in Chapter 12, but it’s a fairly lightweight tool for coding logic in scripts. In its full form, it consists of the word if followed by a test and an associated block of code, one or more optional elif (“else if”) tests and code blocks, and an optional else part, with an associated block of code at the bottom to serve as a default. Python runs the block of code associated with the first test that is true, working from top to bottom, or the else part if all tests are false.
The if, elif, and else parts in the preceding example are associated as part of the same statement because they all line up vertically (i.e., share the same level of indentation). The if statement spans from the word if to the start of the print statement on the last line of the script. In turn, the entire if block is part of the while loop because all of it is indented under the loop’s header line. Statement nesting like this is natural once you get the hang of it.
When we run our new script, its code catches errors before they occur and prints an error message before continuing (which you’ll probably want to improve in a later release), but “stop” still gets us out, and valid numbers are still squared:
Enter text:525 Enter text:xyzBad!Bad!Bad!Bad!Bad!Bad!Bad!Bad! Enter text:10100 Enter text:stop
The preceding solution works, but as you’ll see later in the book, the most general way to handle errors in Python is to catch and recover from them completely using the Python try statement. We’ll explore this statement in depth in Part VII of this book, but as a preview, using a try here can lead to code that some would see as simpler than the prior version:
while True:
reply = input('Enter text:')
if reply == 'stop': break
try:
num = int(reply)
except:
print('Bad!' * 8)
else:
print(num ** 2)
print('Bye')
This version works exactly like the previous one, but we’ve replaced the explicit error check with code that assumes the conversion will work and wraps it in an exception handler for cases when it doesn’t. In other words, rather than detecting an error, we simply respond if one occurs.
This try statement is another compound statement, and follows the same pattern as if and while. It’s composed of the word try, followed by the main block of code (the action we are trying to run), followed by an except part that gives the exception handler code and an else part to be run if no exception is raised in the try part. Python first runs the try part, then runs either the except part (if an exception occurs) or the else part (if no exception occurs).
In terms of statement nesting, because the words try, except, and else are all indented to the same level, they are all considered part of the same single try statement. Notice that the else part is associated with the try here, not the if. As we’ve seen, else can appear in if statements in Python, but it can also appear in try statements and loops—its indentation tells you what statement it is a part of. In this case, the try statement spans from the word try through the code indented under the word else, because the else is indented the same as try. The if statement in this code is a one-liner and ends after the break.
Again, we’ll come back to the try statement later in this book. For now, be aware that because try can be used to intercept any error, it reduces the amount of error-checking code you have to write, and it’s a very general approach to dealing with unusual cases. If we’re sure that print won’t fail, for instance, this example could be even more concise:
while True:
reply = input('Enter text:')
if reply == 'stop': break
try:
print(int(reply) ** 2)
except:
print('Bad!' * 8)
print('Bye')
And if we wanted to support input of floating-point numbers instead of just integers, for example, using try would be much easier than manual error testing—we could simply run a float call and catch its exceptions:
while True:
reply = input('Enter text:')
if reply == 'stop': break
try:
print(float(reply) ** 2)
except:
print('Bad!' * 8)
print('Bye')
There is no isfloat for strings today, so this exception-based approach spares us from having to analyze all possible floating-point syntax in an explicit error check. When coding this way, we can enter a wider variety of numbers, but errors and exits still work as before:
Enter text:502500.0 Enter text:40.51640.25 Enter text:1.23E-1001.5129e-200 Enter text:spamBad!Bad!Bad!Bad!Bad!Bad!Bad!Bad! Enter text:stopBye
Python’s eval call, which we used in Chapter 5 and Chapter 9 to convert data in strings and files, would work in place of float here too, and would allow input of arbitrary expressions (“2 ** 100” would be a legal, if curious, input, especially if we’re assuming the program is processing ages!). This is a powerful concept that is open to the same security issues mentioned in the prior chapters. If you can’t trust the source of a code string, use more restrictive conversion tools like int and float.
Python’s exec, used in Chapter 3 to run code read from a file, is similar to eval (but assumes the string is a statement instead of an expression and has no result), and its compile call precompiles frequently used code strings to bytecode objects for speed. Run a help on any of these for more details; as mentioned, exec is a statement in 2.X but a function in 3.X, so see its manual entry in 2.X instead. We’ll also use exec to import modules by name string in Chapter 25—an example of its more dynamic roles.
Let’s look at one last mutation of our code. Nesting can take us even further if we need it to—we could, for example, extend our prior integer-only script to branch to one of a set of alternatives based on the relative magnitude of a valid input:
while True:
reply = input('Enter text:')
if reply == 'stop':
break
elif not reply.isdigit():
print('Bad!' * 8)
else:
num = int(reply)
if num < 20:
print('low')
else:
print(num ** 2)
print('Bye')
This version adds an if statement nested in the else clause of another if statement, which is in turn nested in the while loop. When code is conditional or repeated like this, we simply indent it further to the right. The net effect is like that of prior versions, but we’ll now print “low” for numbers less than 20:
Enter text:19low Enter text:20400 Enter text:spamBad!Bad!Bad!Bad!Bad!Bad!Bad!Bad! Enter text:stopBye