How to Make Choices
Operators, Expressions and Statements
An expression is a combination of identifiers, values and operators which computes a value. It is quite similar to an arithmetic expression, with the only difference that the scope of programming languages expressions is way bigger since they can compute any kind of data, be they numerical or otherwise. We have already delved into identifiers and values; what we miss to write proper Python expressions are now the operators. They are special symbols and keywords that instruct a computation between two values. The semantics of an operator depends on the type of its operands.
Logical Operators (Yield Boolean)
Logical operators allow to manipulate and chain boolean values. Non-boolean values can also be operands of a logical operator, but they will be implicitly converted to boolean values with the bool() data type constructor before evaluation.
not (unary negation) is used to change the state of a boolean type, from
False. It is like pressing a switch. "Unary" means that the operator only affects the following operand.
or is used to build a logical expression that will evaluate
True if any of the operands is
True. Look at the diagram: the pipe has a fork with a tap on each side. The fork is the "or" operator, and the taps are the operands; if any of the valves are open (
True), the water will flow on the other side.
and (conditional and) is used to build a logical expression that will evaluate
True if every operand is
True. Look at the diagram: a straight pipe represents the boolean expression with two taps following each other. The only way to let the water through is to open both valves. Meaning that every operand has to be
or are also referred to as "short-circuit" operators, because the interpreter will not evaluate the entire expression if unnecessary.
Here is a table showing their behaviours:
Take into account that logical operators accept any type as operands, not only Boolean. If the operand type is non-boolean, the interpreter will convert the expression to a Boolean value and only after that evaluate the logical expression. For example:
|First step||Second step||Third step||Result|
and so on. Check the Boolean Type section if you have any doubt concerning the way
Equality Operators (Yield Boolean)
To make reasonable choices, evaluating if values are the same is very important. Python can test two different notions of “sameness”, equality and identity:
It is essential to understand the difference between the two different notions in order to write reliable and semantic code.
Equality operators investigate the values of the objects referred; they are usually integers, strings, containers:
valueOne = 12 valueTwo = 25 valueOne == valueTwo # False valueOne != valueTwo # True someChars = 'Lemon' moreChars = 'lemon' someChars == moreChars # False, Python is case-sensitive myFirstList = [1, 'a', False] mySecondList = [1, 'a', False] myFirstList == mySecondList # True myThirdList = [2, 'b', None] myFirstList == myThirdList # False
Identity operators instead, check if two identifiers refer or not to the same object. So, in different words, they check if two identifiers are pointing to the same place in memory. Let’s write a little variation of the code above:
myFirstList = [1, 'a', False] mySecondList = myFirstList myThirdList = [1, 'a', False] # two aliases referring to the same object in memory myFirstList is mySecondList # True # extra check id(myFirstList) == id(mySecondList) # True # two aliases referring to two different objects # with the same content myFirstList is myThirdList # False # indeed they have the same content myFirstList == myThirdList # True # but different identity id(myFirstList) == id(myThirdList) # False
So, when should you use what? When comparing numbers, text, containers content is good practice to use
is not, they are ambiguous and often not reliable because of caching. Instead, when comparing identifiers to singletons (
None) you should always use
is not. Why? Because there is only one instance of each of these type (therefore their identity is reliable) and makes your code more readable.
if isDark is True: turnLight()
Comparison Operators (Yield Boolean)
Sorting things is quite a big deal in programming. Python provides a few operators able to detect the size of two expressions:
|less than or equal to|
|greater than or equal to|
x = 6 y = 9 x < y # True x <= y # True x == y # False x >= y # False x > y # False
x = y = 6 x > y # False x < y # False x == y # True x <= y # True x >= y # True
When comparing non-integer instances, they are translated in a numerical value and then evaluated. For example, strings are converted to sequences of integers according to the position of each character into the Unicode standard.
Arithmetic Operators (Yield Floating Point or Integer)
Computers have the ability to compute arithmetical expression very quickly. Python, of course, supports the standard arithmetic operators as follows:
Addition, subtraction and multiplication have a very straightforward behaviour: if both operands are integer, they yield an integer. If one of the operands is a floating point, they will return a floating point value. For example:
2 + 3.5 # 5.5 2-1 # 1 10*3 # 30
Division has two different operators which behave slightly differently. The standard division is what you would expect from it:
6 / 4 # 1.5
If you are allowed to slice units, you can proceed as follows:
The integer division instead yields the mathematical floor of the quotient:
6//4 # 1
if you can’t slice units you group 4 units only once from a collection of 6
The modulo operator yields the remainder of such division:
6 % 4 # 2
In combination with the equivalent (
==) or not equivalent (
!=) operator, the modulo can be really helpful in checking whether a number is even or odd, whether a number is decimal or not and so on
3 % 2 != 0 # True # 3 is odd 2.5 % 1 == 0 # False # 2.5 is not integer
Python provides the opportunity to combine more expressions at once. The result of these compound expressions relies on the evaluation of each single expression within it. The order of evaluation affects the final result, so it is very important to get a grip on precedence standard of the interpreter. For example:
5+2*8 # 21
Because of the higher precedence of multiplication over addition,
2*8 is evaluated first, then
16 is added to
21 as result. In order to manipulate the execution order of evaluation, parenthesis can be used. For example:
(5+2)*8 # 56
Here is a concise table of the expressions evaluation order sorted by higher precedence
|function call, slicing sequence|
As we already mentioned, whitespace is semantic in Python. This means that in order to group lines of code it is necessary to compose the statements which form the block accordingly. Consider the following list of words:
potatoes groceries eggplants tomatoes
Here we are taking no advantage of visual variables in order to make the hierarchy more accessible to readers. We can only rely on the meaning of these words and try to detect a possible structure. So, at first sight, we detect no hierarchy, but after reading, we notice that
groceries can group all the other items.
There are a few contrivances we can implement to express this structure at best using only typography. Let’s start to make some order moving groceries to the top:
groceries potatoes eggplants tomatoes
Then we should find a way to associate unequivocally
tomatoes as part of the same group. A very elegant and economic way is to move their alignment rightwards, like this:
groceries potatoes eggplants tomatoes
After grouping, hierarchy. A very common and shared way of tag the first line of a list as a title is to use the colon sign, like:
groceries: potatoes eggplants tomatoes
If we compare the final outcome with the original one, we will notice a sensible improvement. We do this all day without even noticing. We activate visual variables in order to build images with clear meanings that are easy to access to other people. It is a subtle kind of grammar, but an extremely powerful one.
The designer of Python, Guido van Rossum, decided to implement such behaviour into the Python programming language. This behaviour is mandatory, meaning that the Python interpreter expects the user to indent code semantically in order to organize code blocks. These statements need a colon as final delimiter:
if, elif, else for while with def try, except, finally class
The code related to the statement needs to be indented four spaces rightwards – tab is also accepted by the interpreter but not recommended – in order to be considered as the body of the construct. For example:
if index != 0: print('something') print('something else')
‘something else’ is not part of the conditional construct because it falls out of the indented body. Consider also that Python allows nested structures like:
for eachElem in iterable: if eachElem == target: print 'found!'
In order to execute the conditional construct nested into the for-loop, this has to be indented four spaces more than its parent statement, which is already indented four spaces. Meaning, it has to be indented eight spaces rightwards. Just like a matryoshka.
Now that we have the tools, we need to instruct the interpreter to make reasonable choices. As we already said, it is critical while programming to be able to check whether a condition is true or not and then to be able to reroute the execution flow on different blocks of code.
Here’s a common standard diagram used to describe these patterns is the flowchart:
Python provides a control structure called “conditional construct” which allows to define whether a block of code should be executed or not. The minimal form of a conditional structure is:
if condition: body
Here’s a concrete example:
newPage(100, 100) myVar = 10 if myVar < 20: rect(10, 10, 40, 40)
The condition is a Boolean expression. If this expression results in a
True value, the indented block of code that follows will be executed. The body of the conditional construct can include other nested structures, they only need to be indented accordingly.
newPage(100, 100) myVar = 8 if myVar < 20: if myVar < 10: fill(.5) rect(10, 10, 40, 40)
As soon as a statement falls out of the indented body, it is not considered part of the conditional structure anymore. For example:
newPage(100, 100) myVar = 10 if myVar < 5: fill(1) rect(10, 10, 40, 40)
In this case, whether the body is executed or not, a rectangle will be drawn on the canvas.
else statement can be placed at the end of the conditional construct. The instructions grouped into this statement will be executed if any other statement (
elif) of the conditional construct is not met. Here’s how it is used:
if condition: body1 else: body2
Here’s a concrete example:
newPage(100, 100) myVar = 22 if myVar < 20: rect(10, 10, 40, 40) else: oval(10, 10, 40, 40)
else keyword is not followed by any condition, because it doesn’t need any evaluation. It is an interpreter parachute.
Moreover, Python allows to chain the evaluation of many conditions at once, using an indefinite number of
elif (which means “else if”) statements between
if (mandatory) and
The structure is then:
if condition1: body1 elif condition2: body2 elif condition3: body3 else: # all other possible cases body4
newPage(100, 100) myVar = 10 # chained comparison expressions! # it is helpful when looking for # a value included between values if 20 < myVar: fill(0) elif 20 >= myVar > 12: fill(.3) elif 12 >= myVar > 8: fill(.6) else: fill(.9) rect(10, 10, 50, 50)
Take note of the use of indentation.
else are aligned to the left while their bodies are four spaces indented to the right.
Write a program able to draw a circle positioned at the center of the canvas. Its size should react to a parameter "factor" between 0 and 1:
- if 0, the diameter should be a quarter of the canvas height
- if 1, the diameter should be three-quarters of the canvas height
- anything in between should smoothly interpolate
Define also a variable "switch" pointing to a boolean value. A True value should create a black circle on a white background, otherwise the opposite.
Draw four circles placed on the bottom left – top right canvas diagonal. Circles fill color should change from light to dark grey, following the diagonal direction. Then, assign a boolean value to an identifier called “firstLightThenDark”. This variable should allow the user to decide in which direction the circles should be drawn.
Draw three dots vertically aligned in the middle of a squared canvas. Define a “distance” variable between 0 and 1. This value should push the dots away towards the canvas edges alternatively in opposite direction. Define also a “switch” variable with a boolean value assigned able to flip the dots direction.