8. El control de la computación

Up to now, your short programs are entirely dependent on you for making decisions. This is fine for pieces of text that fit on a single line, but is clearly insufficient for texts that run to hundreds of lines in length. You will want Python to make decisions for you. How to tell Python to do so is the topic of this chapter, and falls under the rubric of control.

8.1. Conditions

8.1.1. How to check for the presence of an item with in

The first step in making a decision is to distinguish those cases in which the decision applies from those in which it does not. In computer science, this is usually known as a condition. Perhaps the simplest condition in text processing is whether an item is present or not. Python handles this in a way that looks a lot like English:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> string = 'Yo!'
>>> 'Y' in string
>>> 'o' in string
>>> '!' in string
>>> 'o!' in string
>>> 'Yo!' in string
>>> 'Y!' in string
>>> 'n' in string
>>> '?' in string
>>> '' in string

The first line assigns a string to a variable, and the rest ask whether a given sub-string is in the string. Python does not answer with “yes” or “no”, but rather with “True” or “False”. I refer to these as the evaluation of the question or condition. The evaluation of each condition should work out exactly as you would imagine – except in the last one. A quirk of the mathematics of strings is that the null string is part of every string. This can trick you up if you are not alert to it.

Lists behave exactly like strings, with the proviso that the string being asked about match a string in the list exactly:

1
2
3
4
5
>>> lst = ['apple', 'cherry', 'mango', 'pear', 'watermelon']
>>> 'apple' in lst
>>> 'peach' in lst
>>> 'app' in lst
>>> '' in lst

Note that the null string is not an element of the list.

Python can understand sequences of in conditions:

1
2
3
4
5
>>> 'app' in 'apple' in lst
#  'app' in 'apple' > True
#  'apple' in lst  > True
>>> 'aple' in 'apple' in lst
>>> 'pea' in 'peach' in lst

The compound condition in the first line evaluates to true, because Python reads it from left to right as in the two comments: Python evaluates any well-formed in condition that it runs across, left to right. The last two lines show that if either in condition comes out false, the whole thing is false.

8.1.2. How to check for the absence of an item with not in

Sometimes you also want to know whether an item is not present. Python effects this with two different orderings of not:

1
2
3
4
5
6
>>> not 'n' in string
>>> 'n' not in string
>>> 'Y' not in string
>>> 'Y!' not in string
>>> 'Yo' not in string
>>> '' not in string

not 'n' in string follows the normal statement of negation in logic, in which the negator applies to the whole statement to be negated, like not('n' in string). 'n' not in string sounds much more like English.

Lists again work just like strings:

1
2
3
4
>>> 'apple' not in lst
>>> 'peach' not in lst
>>> 'app' not in lst
>>> '' not in lst

Compound negations work just as you would expect:

1
2
3
>>> 'pee' not in 'peach' not in lst
>>> 'pea' not in 'peach' not in lst
>>> 'pea' not in 'apple' not in lst

8.1.3. How to check the properties of a string

You can verify the properties of a string with the methods below, whose meaning should be evident from their name:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> S = 'CoNfUsIoN'
>>> S.isalpha()
>>> S.isdigit()
>>> S.isalnum()
>>> S.isspace()
>>> S.islower()
>>> S.isupper()
>>> S.istitle()
>>> S.startswith('CoN')
>>> S.endswith('IoN')

Note that a titlecased string is one which the first letter of each word is upper case and the rest are lower case.

8.1.5. How to compare magnitude with ==, !=, <, <=, >, >=

You can compare the magnitude of two objects with the operators of basic arithmetic:

1
2
3
4
5
6
>>> len(string) == len(string)
>>> len(string) != len(lst)
>>> len(string) < len(lst)
>>> len(string) <= len(lst)
>>> len(lst) > len(string)
>>> (lst) >= len(string)

These operators can be chained together:

>>> len(string) < len(lst) < len(S) == 9

Python also allows comparison of objects with is and is not:

1
2
3
4
>>> string is string
>>> string is not lst
>>> string1 = string
>>> string1 is string

8.1.6. How to combine conditions with and

You can combine conditions with and:

1
2
>>> S.isalpha() and S.startswith('CoN')
>>> not S.isdigit() and not S.startswith('oN')

8.1.7. How to choose conditions with or

You can choose one condition or another with or:

1
2
>>> S.isalpha() or S.isdigit()
>>> S.isupper() or S.islower()

8.2. Conditional expressions

8.2.1. How to make action contingent on a condition with if

Having explored several ways to trigger a decision, we now turn to how to connect a trigger to an action. In English, the simplest way to do so is by saying something like “if condition, then action”. Python tries hard to replicate the naturalness of English. Consider an initial example:

1
2
3
4
>>> if 'N' in S:
...     print 'yes' # you must start line with a tab
...
yes

In the example, 'N' in S is the trigger or condition and print 'yes' is the action that are coordinated by if. The condition must evaluate to true, whether it is positive or negative:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> if 'n' in S:
...     print 'yes'
...
>>>
>>> if 'n' not in S:
...     print 'no'
...
no
>>> if 'N' not in S:
...     print 'no'
...
>>>

If the condition evaluates to false, the trigger fails and no action is taken. Thus the general syntax for if is:

1
2
3
>>> if True:
...     do something
...

Here are some more examples, including regular expressions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
>>> if S.isalpha():
...     print 'yes'
...
yes
>>> if S.endswith('IoN'):
...     print 'yes'
...
yes
>>> if not S.endswith('CoN'):
...     print 'yes'
...
yes
>>> if re.search(r'N', S):
...     print 'yes'
...
yes
>>> if re.search(r'IoN$', S):
...     print 'yes'
...
yes
>>> if not re.search(r'CoN$', S):
...     print 'yes'
...
yes
>>> if len(string) < len(lst):
...     print 'yes'
...
yes
>>> if S.isalpha() or S.isdigit():
...     print 'yes'
...
yes

8.2.2. Chained conditions

Imagine that you wanted to check whether a character is lowercase. You would test for two conditions: whether it is lowercase or whether it is uppercase. But there are a lot of leftovers which are neither one – the punctuation. These three conditions are mutually exclusive, so they cannot be stated as three ifs. A different syntax is necessary, that of the chained conditional expression:

1
2
3
4
5
6
7
8
9
>>> char = 'Y'
>>> if char.islower():
...     print 'yes''
... elif char.isupper():
...     print 'no''
... else:
...     print 'whoops!'
...
no

The chained conditional starts with if` and follows with as many instances of elif as there are alternative conditions. If there is data left but it doesn’t fall under any condition – the remainder – it is captured with else, which lacks a condition.

The upshot is that you can now endow your programs with the ability to take an action on your behalf. Yet this ability is limited to applying to one item at a time. The next section frees you from that limitation.

8.3. Iterating over the items of a collection with a loop

In computer science, the programming construct for examining every item of a collection is called a loop. This could be every character in a string or every word in a text.

8.3.1. How to examine every item with for

As a simple example, consider printing every letter of a word:

1
2
3
4
5
6
7
>>> string = 'Yo!'
>>> for char in string:
...     print char
...
Y
o
!

The syntax of the for statement looks just like that of if. char in word looks like it is is a condition, but after for, it is really the range of items to be iterated over. The variable char` stands for each character of word. Before continuing, it is crucial to point out that, just like with if, the lines after the colon must be indented:

1
2
3
4
5
6
>>> for char in word:
... print char
File "<stdin>", line 2
print char
^
IndentationError: expected an indented block

for applies to lists, too:

1
2
3
4
5
6
7
8
9
>>> lst = ['apple', 'cherry', 'mango', 'pear', 'watermelon'] # 'list' is a reserved word
>>> for word in lst:
...             print word
...
apple
cherry
mango
pear
watermelon

To save space in your Python console, adding a comma after the variable to be printed puts the output on the same line:

1
2
3
4
5
6
7
8
>>> for char in string:
...     print char,
...
Y o !
>>> for word in lst:
...     print word,
...
apple cherry mango pear watermelon

8.3.2. How to make a list during a loop with append()

Just printing the result of a loop to the Python console is rather dull. It would be much more useful to put it into a list for further processing. Python has a method called append() that adds an item to the end of an existing list. To use it, first make an empty list to hold the items, then use append() to add an item to it during each iteration of the loop:

1
2
3
4
5
6
>>> charlist = []
>>> for char in string:
...     charlist.append(char)
...
>>> charlist
['Y', 'o', '!']

The method can be read as “to charlist append char”. You can do the same with lists, but this just turns one list into another:

1
2
3
4
5
6
>>> wordlist = []
>>> for word in lst:
...     wordlist.append(word)
...
>>> wordlist
['apple', 'cherry', 'mango', 'pear', 'watermelon']

8.3.3. How to pack a loop into a list with a list comprehension

Creating a list from a loop is such a frequent task that Python has a breathtakingly elegant idiom for accomplishing it, the list comprehension. It consists of putting the whole for statement within square brackets, with the appending signaled by the brackets themselves. I repeat the previous print loops with their corresponding list comprehension:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
>>> for char in string:
...     print char,
...
Y o !
>>> [char for char in string]
['Y', 'o', '!']

>>> for word in lst:
...     print word,
...
apple cherry mango pear watermelon
>>> [word for word in lst]
['apple', 'cherry', 'mango', 'pear', 'watermelon']

The first list comprehension converts the string to a list; the second one doesn’t change anything because lst is already a list. These list comprehensions can be read in English along the lines of “the list of characters such that each one is in string” and “the list of words such that each one is in lst”.

8.3.4. The list comprehension in set theory

The format of the list comprehension is inspired on a similar expression in set theory:

{e | e ∈ F & P(e)}

This is read as “the set of e’s such that e is an element of F and P of e (e has the property P)”.

8.3.5. How to check a condition in a loop

The ultimate step in making a decision about a collection of items is to make membership in the output contingent on a condition:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
>>> for char in string:
...     if char.islower():
...             print char,
...
o
>>> [char for char in string if char.islower()]
['o']
>>> for word in list:
...     if word.endswith('melon'):
...             print word,
...
watermelon
>>> [word for word in list if word.endswith('melon')]
['watermelon']

Again, in English the list comprehensions could be read as “the list of characters such that each one is in string and is lower case” and “the list of words such that each one is in list and ends with ‘melon’”.

Chained conditionals can also be put into a loop, which makes them much easier to use:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> for char in string:
...     if char.islower():
...             print 'yes'
...     elif char.isupper():
...             print 'no'
...     else:
...             print 'whoops!''
...
yes
no
whoops!

However there is no list comprehension that is exactly analogous to a chained conditional, since elif is not allowed in them. A list comprehension only allows if -- else, so the elif has to be decomposed into else -- if. Here is what it looks like in a loop:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> for char in string:
...     if char.islower():
...             print 'yes'
...     else:
...             if char.isupper():
...                     print 'no'
...             else:
...                     print 'whoops!''
...
yes
no
whoops!

Here is what it looks like in a list comprehension:

1
2
>>> ['yes' if char.islower() else 'no' if char.isupper() else 'whoops!' for char in string]
['no', 'yes', 'whoops!']

This can be read as “the list of yeses if the character is lowercase, noes if the character is uppercase and whoopses otherwise, for each character in string”.

The closest analogy to a three-part conditional is to populate a list with append() inside each condition:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
>>> under5 = []; equal5 = []; over5 = []
>>> for word in lst:
...     if len(word) < 5:
...             under5.append(word)
...     elif len(word) > 5:
...             over5.append(word)
...     else:
...             equal5.append(word)
...
>>> under5; equal5; over5
['pear']
['apple', 'mango']
['cherry', 'watermelon']

8.3.6. How to transform the items within a loop

print also prints expressions:

1
2
3
4
5
6
7
8
>>> for char in string:
...     print char.upper(),
...
Y O !
>>> for word in lst:
...     print len(word),
...
5 6 5 4 10

This can be done in a list comprehension by transforming the first mention of the variable:

1
2
3
4
>>> [char.upper() for char in string]
['Y', 'O', '!']
>>> [len(word) for word in lst]
[5, 6, 5, 4, 10]

In English, we would say, “the list of characters converted to upper case such that each character is in string” and “the list of lengths of word such that each word is in list”.

8.4. Summary

8.5. Further practice

Make an alphabetized list of the words from La gitanilla that:

  1. end in azo.
  2. begin with en or em.
  3. begin with en or em and are more than 2 letters long (exclude ‘en’).
  4. have 8 or more letters.
  5. have more than 5 letters but less than 9.
  6. are forms of salir but not saltar. (continue Prueba P3.2).
  7. end in ón but not ión (ó is u’\xf3).
  8. have an underscore (at the beginning or end).
  9. have any non-alphanumeric character.
  10. Make a list for each form of the suffix ill, masculine singular, feminine singular, masculine plural, feminine plural.

For each one, give a version with and without a regular expression. The answers are found at Answers to further practice with control, but try to do them all before looking at the answers.

8.6. Further reading

8.7. Appendix

Footnotes


Last edited: February 10, 2015