Booleans in Python (Part 2)

Example: checking for a vowel

Let's write some Boolean functions. As our first example, let's write a function that determines if a character is a vowel. To match the names of the built-in Python functions, we'll call it is_vowel and we'll choose a parameter name that reminds us that the input should be a single character.

def is_vowel(char):
    is_a = char == "a" or "A"
    is_e = char == "e" or "E"
    is_i = char == "i" or "I"
    is_o = char == "o" or "O"
    is_u = char == "u" or "U"
    return is_a or is_e or is_i or is_o or is_u

Since there are 10 characters to check, let's break it up into separate lines for the sake of readability.


print(is_vowel("a"))

We can now try out the function on a vowel, which gives the correct answer, True, on running the program.


print(is_vowel("b"))

We can also try it on a consonant, which gives a confusing answer, A, on running the program. What happened?


We will use the technique of tracing the code to see what values were computed.

Definition

To trace code is to figure out values step by step.

Let's look a little more closely at the first line:

def is_vowel(char):
    is_a = char == "a" or "A"
    is_e = char == "e" or "E"
    is_i = char == "i" or "I"
    is_o = char == "o" or "O"
    is_u = char == "u" or "U"
    return is_a or is_e or is_i or is_o or is_u

The variable is_a is assigned the or of two expressions. The first expression is char == "a". This has the value False. The second expression is the string consisting of "A". Since it has the value True, is_a has the value "A". Similarly, each of the following variables were assigned values:

is_a = "A"
is_e = "E"
is_i = "I"
is_o = "O"
is_u = "U"

Since Python uses short-cut evaluation, in the final expression it only needed to read the first value, determine it had Boolean value True, and then stop:

return "A" or "E" or "I" or "O" or "U"

It returned the last and only value it read.

Revisiting expressions using non-Booleans

Let's take a look at how Python handles expressions with non-Booleans. Due to short-cut evaluation, as soon as the computer sees a value with the Boolean value True, like 1, it returns the True value. For example, running the code

print(0 or 1 or 0)

outputs 1.

Consider the following code:

print(0 or 0 or "")

This outputs the empty string "". All of these values are False, but the computer can't conclude that the condition is False until it sees them all. So it returns the last False value it reads, namely the empty string. Put in another way, the computer returns the first True value it finds, like in the following code and its output, or the last False one if all are False, as in the previous case: the code

print(0 or 0 or "not zero")

outputs not zero.

With and, short-cut evaluation means returning the first False value that is found, or the last True value if all are True. So when the code

print("True" and 0 and "False")

is run, 0 is returned. When the code

print("True" and "True" and "spaghetti")

is run, we get the output spaghetti because it is the last of three True values. All of the values are examined in the example code

print("True" and "True" and 0)

giving the output 0. The last value in the expression is the first False value found.

We're now ready to revisit the use of the strings True and False with the Boolean functions or and and. Let's consider the following code

print("True" or "False")  
print("False" or "True")  
print("False" and "True") 
print("True" and "False")

which outputs the following:

True
False
True
False

In the first two examples, the first value is returned since short-cut or returns the first True value, if any. In the last two examples, the last value is returned since short-cut and returns the last True value if there are no False values. Now we can see what happened when we made our error in the function is_vowel(): the first True value was returned, since the code

print("a" == "a" or "A")
print("b" == "a" or "A")
gives the output
True
A

Short-cut evaluation returns:

  • For or, the first True value, if any.
  • For or, the last False value, if no True values.
  • For and, the first False value, if any.
  • For and, the last True value, if no False values.

Keep this in mind if funny answers occur. In a True expression, or returns the first value with Boolean value True and otherwise the last False value. In a False expression, and returns the first value with Boolean value False and otherwise the last True value.

Checking for a vowel, corrected

We can now fix our function to include the correct expressions, namely comparing the input to a lower case vowel and comparing the input to an upper case vowel:

def is_vowel(char):
    is_a = char == "a" or char == "A"
    is_e = char == "e" or char == "E"
    is_i = char == "i" or char == "I"
    is_o = char == "o" or char == "O"
    is_u = char == "u" or char == "U"
    return is_a or is_e or is_i or is_o or is_u

Caution

Not all uses of or and and in English translate well into code.

This is an easy mistake to make, since we would say “Is the input a lower case a or an upper case A?” Double-check any code you write that seems like a straightforward translation from English and make sure it fits what the computer will understand.

Glimpse of the future

More succinct solutions exist.

Later we'll see other shorter ways of writing this function.

Example: checking for a consonant

What if we want to write a function that checks if a character is a consonant? If we use the same method as for checking a vowel, we'll have 42 characters to check - that's a lot of writing to do! Why not use is_vowel instead?

def is_consonant(char):
    return not is_vowel(char)

Does this work for all strings? No, since we could have numbers or symbols. What about the following code?

def is_consonant(char):
    return char.isalpha() and not is_vowel(char)

What if we have a string of length more than one? The following code works if the input is a string, but not if it could be any input:

def is_consonant(char):
    return len(char) == 1 and \
           char.isalpha() and not is_vowel(char)

Caution

When using not, be sure what all the possibilities are.

You can think of not as splitting the universe into “those for which this is true” and “those for which it is not true”. You need to understand what the universe is to be sure that you obtain the behaviour you want.