2.1. Transforming Simple Data with Expressions

2.1.1. Statements and Expressions

A statement is an instruction that the Python interpreter can execute. We have only seen the assignment statement so far. Some other kinds of statements that we’ll see shortly are while statements, for statements, if statements, and import statements. (There are other kinds too!)

An expression is a combination of values, variables, operators, and calls to functions. Expressions need to be evaluated. If you ask Python to print an expression, the interpreter evaluates the expression and displays the result.

In [1]: print(1 + 1)

In [1]: print(len("hello"))

In this example len is a built-in Python function that returns the number of characters in a string. We’ve previously seen the print and the type functions, so this is our third example of a function!

The evaluation of an expression produces a value, which is why expressions can appear on the right hand side of assignment statements. A value all by itself is a simple expression, and so is a variable. Evaluating a variable gives the value that the variable refers to.

In [1]: y = 3.14

In [1]: x = len("hello")

In [1]: x

In [1]: y

Note that when we enter the assignment statement, y = 3.14, only the prompt is returned. There is no value. This is due to the fact that statements, such as the assignment statement, do not return a value. They are simply executed.

On the other hand, the result of executing the assignment statement is the creation of a reference from a variable, y, to a value, 3.14. When we execute the print function working on y, we see the value that y is referring to. In fact, evaluating y by itself results in the same response.

2.1.2. Operators and Operands

Operators are special tokens that represent computations like addition, multiplication and division. The values the operator works on are called operands.

The following are all legal Python expressions whose meaning is more or less clear:

20 + 32
hour - 1
hour * 60 + minute
minute / 60
5 ** 2
(5 + 9) * (15 - 7)

The tokens +, -, and *, and the use of parenthesis for grouping, mean in Python what they mean in mathematics. The asterisk (*) is the token for multiplication, and ** is the token for exponentiation. Addition, subtraction, multiplication, and exponentiation all do what you expect.

In [1]: 2 + 3

In [1]: 2 - 3

In [1]: 2 * 3

In [1]: 2 ** 3

In [1]: 3 ** 2

When a variable name appears in the place of an operand, it is replaced with the value that it refers to before the operation is performed. For example, what if we wanted to convert 645 minutes into hours. In Python 3, division is denoted by the operator token / which always evaluates to a floating point result.

In [1]: minutes = 645

In [1]: hours = minutes / 60

In [1]: hours

What if, on the other hand, we had wanted to know how many whole hours there are and how many minutes remain. To help answer this question, Python gives us a second flavor of the division operator. This version, called integer division, uses the token //. It always truncates its result down to the next smallest integer (to the left on the number line).

In [1]: 7 / 4

In [1]: 7 // 4

In [1]: minutes = 645

In [1]: hours = minutes // 60

In [1]: hours

Pay particular attention to the first two examples above. Notice that the result of floating point division is 1.75 but the result of the integer division is simply 1. Take care that you choose the correct flavor of the division operator. If you’re working with expressions where you need floating point values, use the division operator /. If you want an integer result, use //.

The modulus operator, sometimes also called the remainder operator or integer remainder operator works on integers (and integer expressions) and yields the remainder when the first operand is divided by the second. In Python, the modulus operator is a percent sign (%). The syntax is the same as for other operators.

In [1]: quotient = 7 // 3     # This is the integer division operator

In [1]: quotient

In [1]: remainder = 7 % 3

In [1]: remainder

In the above example, 7 divided by 3 is 2 when we use integer division and there is a remainder of 1.

The modulus operator turns out to be surprisingly useful. For example, you can check whether one number is divisible by another—if x % y is zero, then x is divisible by y. Also, you can extract the right-most digit or digits from a number. For example, x % 10 yields the right-most digit of x (in base 10). Similarly x % 100 yields the last two digits.

Finally, returning to our time example, the remainder operator is extremely useful for doing conversions, say from seconds, to hours, minutes and seconds. If we start with a number of seconds, say 7684, the following program uses integer division and remainder to convert to an easier form. Step through it to be sure you understand how the division and remainder operators are being used to compute the correct values.

(ch02_19_codelens)

Check your understanding

    data-2-26: What value is printed when the following statement executes?

    print(18 / 4)
    
  • (A) 4.5
  • The / operator does exact division and returns a floating point result.
  • (B) 5
  • The / operator does exact division and returns a floating point result.
  • (C) 4
  • The / operator does exact division and returns a floating point result.
  • (D) 2
  • The / operator does exact division and returns a floating point result.

    data-2-27: What value is printed when the following statement executes?

    print(18 // 4)
    
  • (A) 4.25
  • - The // operator does integer division and returns an integer result
  • (B) 5
  • - The // operator does integer division and returns an integer result, but it truncates the result of the division. It does not round.
  • (C) 4
  • - The // operator does integer division and returns the truncated integer result.
  • (D) 2
  • - The // operator does integer division and returns the result of the division on an integer (not the remainder).

    data-2-28: What value is printed when the following statement executes?

    print(18 % 4)
    
  • (A) 4.25
  • The % operator returns the remainder after division.
  • (B) 5
  • The % operator returns the remainder after division.
  • (C) 4
  • The % operator returns the remainder after division.
  • (D) 2
  • The % operator returns the remainder after division.

2.1.3. Order of Operations

When more than one operator appears in an expression, the order of evaluation depends on the rules of precedence. Python follows the same precedence rules for its mathematical operators that mathematics does.

  1. Parentheses have the highest precedence and can be used to force an expression to evaluate in the order you want. Since expressions in parentheses are evaluated first, 2 * (3-1) is 4, and (1+1)**(5-2) is 8. You can also use parentheses to make an expression easier to read, as in (minute * 100) / 60, even though it doesn’t change the result.
  2. Exponentiation has the next highest precedence, so 2**1+1 is 3 and not 4, and 3*1**3 is 3 and not 27. Can you explain why?
  3. Multiplication and both division operators have the same precedence, which is higher than addition and subtraction, which also have the same precedence. So 2*3-1 yields 5 rather than 4, and 5-2*2 is 1, not 6.
  4. Operators with the same precedence are evaluated from left-to-right. In algebra we say they are left-associative. So in the expression 6-3+2, the subtraction happens first, yielding 3. We then add 2 to get the result 5. If the operations had been evaluated from right to left, the result would have been 6-(3+2), which is 1.

Note

Due to some historical quirk, an exception to the left-to-right left-associative rule is the exponentiation operator **. A useful hint is to always use parentheses to force exactly the order you want when exponentiation is involved:

In [1]: 2 ** 3 ** 2     # the right-most ** operator gets done first!

In [1]: (2 ** 3) ** 2   # use parentheses to force the order you want!

Check your understanding

    data-2-29: What is the value of the following expression:

    16 - 2 * 5 // 3 + 1
    
  • (A) 14
  • Using parentheses, the expression is evaluated as (2*5) first, then (10 // 3), then (16-3), and then (13+1).
  • (B) 24
  • Remember that * has precedence over -.
  • (C) 3
  • Remember that // has precedence over -.
  • (D) 13.667
  • Remember that // does integer division.

    data-2-30: What is the value of the following expression:

    2 ** 2 ** 3 * 3
    
  • (A) 768
  • Exponentiation has precedence over multiplication, but its precedence goes from right to left! So 2 ** 3 is 8, 2 ** 8 is 256 and 256 * 3 is 768.
  • (B) 128
  • Exponentiation (**) is processed right to left, so take 2 ** 3 first.
  • (C) 12
  • There are two exponentiations.
  • (D) 256
  • Remember to multiply by 3.

2.1.4. Boolean Values and Boolean Expressions

The Python type for storing true and false values is called bool, named after the British mathematician, George Boole. George Boole created Boolean Algebra, which is the basis of all modern computer arithmetic.

There are only two boolean values. They are True and False. Capitalization is important, since true and false are not boolean values (remember Python is case sensitive).

In [1]: True

In [1]: type(True)

In [1]: type(False)

Note

Boolean values are not strings!

It is extremely important to realize that True and False are not strings. They are not surrounded by quotes. They are the only two values in the data type bool. Take a close look at the types shown below.

In [1]: type(True)

In [1]: type("True")

A boolean expression is an expression that evaluates to a boolean value. The equality operator, ==, compares two values and produces a boolean value related to whether the two values are equal to one another.

In [1]: 5 == 5

In [1]: 5 == 6

In the first statement, the two operands are equal, so the expression evaluates to True. In the second statement, 5 is not equal to 6, so we get False.

The == operator is one of six common comparison operators; the others are:

x != y               # x is not equal to y
x > y                # x is greater than y
x < y                # x is less than y
x >= y               # x is greater than or equal to y
x <= y               # x is less than or equal to y

Although these operations are probably familiar to you, the Python symbols are different from the mathematical symbols. A common error is to use a single equal sign (=) instead of a double equal sign (==). Remember that = is an assignment operator and == is a comparison operator. Also, there is no such thing as =< or =>.

Note too that an equality test is symmetric, but assignment is not. For example, if a == 7 then 7 == a. But in Python, the statement a = 7 is legal and 7 = a is not. (Can you explain why?)

Check your understanding

    data-2-31: Which of the following is a Boolean expression? Select all that apply.
  • (A) True
  • True and False are both Boolean literals.
  • (B) 3 == 4
  • The comparison between two numbers via == results in either True or False (in this case False), both Boolean values.
  • (C) 3 + 4
  • 3 + 4 evaluates to 7, which is a number, not a Boolean value.
  • (D) 3 + 4 == 7
  • 3 + 4 evaluates to 7. 7 == 7 then evaluates to True, which is a Boolean value.
  • (E) "False"
  • With the double quotes surrounding it, False is interpreted as a string, not a Boolean value. If the quotes had not been included, False alone is in fact a Boolean value.

2.1.5. Logical operators

There are three logical operators: and, or, and not. The semantics (meaning) of these operators is similar to their meaning in English. For example, x > 0 and x < 10 is true only if x is greater than 0 and at the same time, x is less than 10. How would you describe this in words? You would say that x is between 0 and 10, not including the endpoints.

n % 2 == 0 or n % 3 == 0 is true if either of the conditions is true, that is, if the number is divisible by 2 or divisible by 3. In this case, one, or the other, or both of the parts has to be true for the result to be true.

Finally, the not operator negates a boolean expression, so not  x > y is true if x > y is false, that is, if x is less than or equal to y.

In [1]: x = 5

In [1]: x > 0 and x < 10

In [1]: n = 25

In [1]: n % 2 == 0 or n % 3 == 0

Common Mistake!

There is a very common mistake that occurs when programmers try to write boolean expressions. For example, what if we have a variable number and we want to check to see if its value is 5,6, or 7. In words we might say: “number equal to 5 or 6 or 7”. However, if we translate this into Python, number == 5 or 6 or 7, it will not be correct. The or operator must join the results of three equality checks. The correct way to write this is number == 5 or number == 6 or number == 7. This may seem like a lot of typing but it is absolutely necessary. You cannot take a shortcut.

Check your understanding

    data-2-32: What is the correct Python expression for checking to see if a number stored in a variable x is between 0 and 5.
  • (A) x > 0 and < 5
  • Each comparison must be between exactly two values. In this case the right-hand expression < 5 lacks a value on its left.
  • (B) 0 < x < 5
  • This is tricky. Although most other programming languages do not allow this syntax, in Python, this syntax is allowed. However, you should not use it. Instead, make multiple comparisons by using and or or.
  • (C) x > 0 or x < 5
  • Although this is legal Python syntax, the expression is incorrect. It will evaluate to true for all numbers that are either greater than 0 or less than 5. Because all numbers are either greater than 0 or less than 5, this expression will always be True.
  • (D) x > 0 and x < 5
  • Yes, with an and keyword both expressions must be true so the number must be greater than 0 an less than 5 for this expression to be true.

2.1.6. Precedence of Operators

We have now added a number of additional operators to those we learned in the previous chapters. It is important to understand how these operators relate to the others with respect to operator precedence. Python will always evaluate the arithmetic operators first (** is highest, then multiplication/division, then addition/subtraction). Next comes the relational operators. Finally, the logical operators are done last. This means that the expression x*5 >= 10 and y-6 <= 20 will be evaluated so as to first perform the arithmetic and then check the relationships. The and will be done last. Although many programmers might place parenthesis around the two relational expressions, it is not necessary.

The following table summarizes the operator precedence from highest to lowest. A complete table for the entire language can be found in the Python Documentation.

Level Category Operators
7(high) exponent **
6 multiplication *,/,//,%
5 addition +,-
4 relational ==,!=,<=,>=,>,<
3 logical not
2 logical and
1(low) logical or

Note

This workspace is provided for your convenience. You can use this activecode window to try out anything you like.

Check your understanding

    data-2-33: Which of the following properly expresses the precedence of operators (using parentheses) in the following expression: 5*3 > 10 and 4+6==11
  • (A) ((5*3) > 10) and ((4+6) == 11)
  • Yes, * and + have higher precedence, followed by > and ==, and then the keyword "and"
  • (B) (5*(3 > 10)) and (4 + (6 == 11))
  • Arithmetic operators (*, +) have higher precedence than comparison operators (>, ==)
  • (C) ((((5*3) > 10) and 4)+6) == 11
  • This grouping assumes Python simply evaluates from left to right, which is incorrect. It follows the precedence listed in the table in this section.
  • (D) ((5*3) > (10 and (4+6))) == 11
  • This grouping assumes that "and" has a higher precedence than ==, which is not true.

2.1.7. Conditional Execution: Binary Selection

In order to write useful programs, we almost always need the ability to check conditions and change the behavior of the program accordingly. The simplest form of selection is the if expression. This is sometimes referred to as binary selection since there are two possible paths of execution.

In [1]: x = 15

In [1]: "even" if x % 2 == 0 else "odd"

The syntax for an if expression looks like this:

EXPRESSION_1 if BOOLEAN EXPRESSION else EXPRESSION_2

The boolean expression after the if statement is called the condition. If it is true, then the first expression is evaluated. If not, then the expression after the else clause gets evaluated.

An important property of the conditional expression is that exactly one of the expressions is evaluated and not the other. This allows us to avoid errors that would otherwise be impossible to avoid, as illustrate by the following silly example.

In [1]: x = 15

In [1]: "No Error" if x == 15 else x / 0

If the conditional expression evaluated both expressions, then we would have encountered the following exception.

In [1]: 15 / 0

There is a limitation to the conditional expression. Since this is an expression, it can only contain values or other expressions. This means that we cannot include statements, such as the assignment statement in this expression. In a later section, we will introduce the if-elif-else statement to deal with this limitation.

In [1]: 3 if 5 == 4 else (x = 1)

Check your understanding

    data-2-34: How many statements can appear in each expression (the if and the else) in a conditional expression?
  • (A) Just one.
  • The conditional expression may only consist of other expressions.
  • (B) Zero or more.
  • The conditional expression may only consist of other expressions.
  • (C) One or more.
  • The conditional expression may only consist of other expressions.
  • (D) None.
  • The conditional expression may only consist of other expressions.

    data-2-35: What is the value of the following expression?

    True if 4 + 5 == 10 else False
    
  • (A) True
  • True is only evaluated if the conditional (in this case, 4+5 == 10) is true. In this case 5+4 is not equal to 10.
  • (B) False
  • Since 4+5==10 evaluates to False, Python will skip over the if expression and execute the expression after the else.
  • (C) True on one line and False on the next
  • Python would never evaluate both True and False because it will only evalutate one expression, but not both.
  • (D) Nothing
  • Python will always evaluate either the if-expression or the else-expression. It would never skip over both expressions.
Next Section - 2.2. Saving Expressions for Later Execution - The lambda Expression