Expressions
0. Learning objectives
- Define expressions
- Define and list Python types
- Define and list operators
1. Expressions
You probably used a calculator before to perform simple computation like 2 + 3 (and probably more
complex ones as well!). Well, the Python interpreter can be used just like a calculator as well! We can type in
expressions and, as long as we are using correct Python syntax, the interpreter will show the result. Try
it out with Brython at the bottom of the page! Enter 2 + 3 then hit enter. You should see a
5 printed on the next line.
Note: we mentioned before that, in a moment of extreme creativity, it was decided to name Python both the language and the interpreter. I'll leave it ot you to deduct form the context which one we are talking about.
|
When we typed Python keeps doing this over and over again, as long as there are expressions to evaluate. It is almost like a cycle, a loop. This cycle is known as the Read-Evaluate-Print Loop (this where REPL comes from!). This is kind of like the eat-sleep-repeat meme that you might have seen before. If you ask me, that sounds unhealthy. Programming is fun, but it is more fun when it is part of a balanced lifestyle! What do you think? What happens if you put a bunch of spaces in an expression? Try typing
|
|
2. Types
When Python sees the 2 + 3 in the example above, it looks at the types of the elements in
the expression (2 and 3) and checks if it know how to perform the required operation
(+) between those elements. Depending on the types, Python may need to do something
different in order to add them together. For example, try 2 + 'apple' in Brython. You should
receive an error: TypeError: unsupported operand type(s) for + : 'int' and 'str'.
Python is telling us that we have a TypeError. Specifically, it is telling us that it
doesn't know how to use the operator + with the operands of type int and type
str.
What does that mean??
The int means that the operand we wrote to the left of the + was an
integer and the operands we wrote to the right of the +) was a string. (We'll
talk more about strings in a few lectures - for now, just think of it as sequence of character - some
text). Basically, we asked Python to add an integer (a number) with a string (some text) and it said "hey,
I don't know how to do this!". In other words, nobody has told Python how to add something of type
int to something of typestr.
You will probably come across TypeError errors (and not only) when working on your code (I still
do!). Make sure that you read Python's error messages carefully! The error message is usually very
helpful and gives you information about which file and line number caused the error and it is one
of the first hints about how to debug your code.
Luckily, there is a way for us to figure out the type of a variable. Python has this really useful
function called type(). This function (we'll talk about functions soon) takes and argument
and returns its type. For now, we will mostly use the following types:
|
We can also use type() to determine the type of the result of an expression. Python will evaluate
the expression between the parentheses, then feed the result of the evaluation to the function
type(). Try typing type(2 + 3) in Brython - you should still see
<class 'int'>.
It is also possible to convert between types. Try the following in Brython and look at the type()
of the results:
int(34.6)(and checktype(int(34.6)))int(-4.3)float(21)float('42')float(21)str(-11)
int(), float(), and str are examples of other functions.
And for something even more interesting, try to check what happens if we convert strings and numbers to Boolean
using the bool() function: bool(0) vs. bool(42) (or any other number that
is not 0), or bool('hello!') vs. bool('') (or any other string that is
not the empty string ''). More about strings later!
# (colloquially, the
hashtag) is a comment and tells Python not to try to interpret anything after the hashtag. Just ignore
it! Comments are extremely useful for humans, but useless to Python. You should use comments in your codes
as much as possible.It's not only useful for someone else reading your code, but useful for you once you get back to your code later on and you try to figure out what you were thinking!
3. Operators
In the last example, we were just adding numbers together, but now let's try doing something a little more
interesting. To do more complex calculations, we need additional operators like +. We can
start by thinking of arithmetic operators, comparative operators and logical operators.
Most of these operators will have left-hand-side and right-hand-side operands, perform a specific
operation on them, and then "return" the result of the operation they performed. Parentheses () and
negation - are a little different, since negation only takes in a right-hand side operand and
parentheses allow us to alter the operator precedence by grouping expressions.
If we simply evaluate left-to-right, as we stated Python does, then we should get:
5 * 2 + 6 / 3 - 4 =
= 10 + 6 / 3 - 4 =
= 16 / 3 - 4 =
= 5.33 - 4 = 1.33
But we know that is not the case and that the right result is:
5 * 2 + 6 / 3 - 4 =
= 10 + 6 / 3 - 4 =
= 10 + 2.0 - 4 =
= 12.0 - 4 = 8.0
Note the 6 / 3 = 2.0. The division operator (/) always results in a floating
point number!
But why is the result 8.0 instead of 1.33? Operator precedence!
When using these operators, you need to be careful about operator precedence, i.e. the order in which Python will execute operations if you do not use parentheses to alter that order.
Python follows a specific order when evaluating operators in expressions. The table below illustrates, in order from the highest precedence to the lowest the order in which operators are evaluated within an expression. (Note that operators at the same level of precedence are evaluated left to right ).
| Symbol | Type | Name | Example |
() |
arithmetic | grouping | (2 + 3) |
** |
arithmetic | exponentiation (raise to the power) | 2**4 |
- |
arithmetic | negation (not subtraction) | -3 + 2 |
*, /, %, // |
arithmetic | multiply, divide, modulo, floor division | 4 * 5, 5/2, 11 % 2, 11 // 2 |
+, - |
arithmetic | addition, subtraction | 2 + 3, 7 - 3 |
<, >, <=, >=, ==, != |
comparison | less, greater, less or equal, greater or equal, equal, not-equal | 2 < 3, 2 <= 3, 2 == 'dog' , 2 != 'dog' |
not |
logical | logical NOT | not 2 == 'dog' |
and |
logical | logical AND | (2 == 3) and (2 == 4) |
or |
logical | logical OR | (2 == 3) or (2 == 4) |
= |
assignment | assignment | x = 2 |
Try each of the examples above in Brython and guess the result before you hit enter!
Arithmetic operators allow us to create expressions that involve addition, multiplication, division,
negation, exponentiation. We can also group expressions using parentheses () to override or
clarify the order in which expressions are evaluated. If there are no parentheses, the order follows the
mnemonic PEMDAS: Parentheses are first evaluated, then Exponents, Multiplication,
Division, Addition and Subtraction. You might or might now have heard of PEMDAS (since it
is based on the English language). If not, it is a nice mnemonic that, in doubt, will help you decide which
operator should be executed first in your expression. Having said that, if you are writing long and complex
expressions, do not rely on the order of operations! It really takes absolutely no time to use
parentheses in your code (they are free!) to make it much easier to read for everyone without having to
try to remember which operator comes first. Unless is obvious, use parentheses to specify the order you want!
In Python, exponentiation uses the ** operator. For example, if you type 2**4 in
Brython you should see 16
The negation operator needs little introduction: it will negate the number that follows.
-16 because exponentiation has precedence! If you want to use -2 as base, then
you need to use parentheses: (-2)**4.
Next, we have the multiplicative operators, which includes multiplication (*),
division (/), modulo (%) and integer (floor) division
(//).
Multiplication and division do exactly what they sound like.
The modulo operator can be a bit confusing. For example a % b returns the remainder
after evaluating the integer division a // b. (Make sure to use two forward slashes
to distinguish it from regular division!) The integer division is the smallest integer (hence the name
floor division) you obtain when dividing the two operands.
For example, 5 // 2 = 2 and 5.999999999 // 2 = 2.0 (Notice that, differently from
the division operator / which always returns a floating point value, the floor division
// will return an integer if both operands are integers and a floating point otherwise.).
a = b * ( a // b ) + ( a % b )
Pick two numbers and try the different parts of the expression on the right side of the equation above in
Brython! For a = 7 and b = 2, you should get 7.
Be careful when using the modulo with negative numbers since the results might not be what you expect. In This
case it is useful to remember that Python will always round the result of the floor division down to the
smallest integer (i.e., towards negative infinity). For example, -5 // 2 = -3 (and not
-2) since -3 is smaller than -2. Also, the result of the modulo will
always have the same sign as the divisor (the second operand). For example, -5 % 2 = 1 (and not
-1) since the divisor is 2 (positive) and not -2 (negative).
True or False. For example, 2 < 3 is True since 2 is "less
than" 3. Also, 2 > 2 is False (2 is not "greater than" 2), but 2 >= 2 is
True since 2 is "less than or equal to 2".
When checking if two variables are equal you must use two equals (==) symbols. Not
doing so, would be an assignment and is a very common source of bugs! For
example 2 == 2 is True but 2 == 3 is False, and
2 = 2 results in a SyntaxError.
We can also compare strings: 'dog' == 'apple' is False but
'dog' == 'dog' is True. If you want to check if two things are not equal, then
you can use an exclamation mark with an equals sign (!=). For example 2 != 2 is
False but 2 != 3 is True.
Python also follows the lexicographic order for
strings (more about strings later) that, for us, means that it can evaluate the alphabetical ordering of words
(just like in a dictionary). So, if you ask Python if 'cat' < 'dog', the answer is going to be
True (don't assign to the %lt; operator more meaning than it holds! Python really
doesn't have a favorite between cats and dogs!). Try other examples in Brython to get a feeling about how this
ordering works. Since you can also have numbers (and more) in string (e.g., '123ab23AF' is a valid
string), try to do some detective work and answer these questions. What come first?
- Upper case or lower case letters?
- Numbers or letters?
We will get an explanation for this order once we discuss how computers represent data!
Logical operators allow us combine expressions from which we expect a Boolean value. These operators
actually look like words in Python: and, or and not. Even though these
are words, you should still think of them as having operands on both sides (except for the not
operator which only has a right-hand side operand). The behavior of these operators is better understood if we
evaluate a logic operators based on all the possible operands values. This is typically done using something
that we call a truth table. These can be applied to a single operator or to a more complex logic
expression (an expression where all the operators are logic operators and all the operands are Boolean values).
In the example below, we list all possible Boolean values of A and B (our operands) in
the first and second columns, and then the value resulting from applying the logical not,
and and or operators to these values.
A |
B |
not A |
A and B |
A or B |
False |
False |
True |
False |
False |
False |
True |
True |
False |
True |
True |
False |
False |
False |
True |
True |
True |
False |
True |
Ture |
Observe that:
-
notbasically flips the Boolean value: what isTruebecomesFalseand vice versa. -
andmeans "only evaluate toTrueif both the left- and right-hand operands areTrue, otherwise evaluate toFalse." - Finally, the logical
oris different than the "or" you will say everyday. For example, you might say "I will either eat cereal or I will eat a peanut butter sandwich" - it's clear that you will eat only one of these, but not both (this is what we call an exclusiveor). The logicaloroperator is a bit different: it evaluates toTrueif at least one of the operands isTrueand evaluates toFalseotherwise, and that is what we call an inclusiveor).
Ok, here is an interesting problem. Now that you have the truth table of the fundamental logic operators, can you write the truth table for the following expression?
(A and (not B)) or ((not A) and B)
How do we start? First you should identify the operands. What are the values that might change in this
expression? Then we start a table where we have one column for each of the operands (same name, same
operand). Then we need a column for the value of the entire expression. As the operands take all the
possible values of True and False, you evaluate the value of the expression.
To make your life easier, you can definitely add more columns to the table each representing a
smaller part of the expression, just like the interpreter would do. So, for example, you could have
a first column for not B, then maybe a second on for A and (not B), then a
third for not A, a fourth for (not A) and B, and finally the one for the
entire expression. In this way you isolate simple logic operations for which you already know the
truth table and you can use the results that you already know to find your final result by
composing the intermediate results.Composition is a powerful concept in Computer
Science that allows you to build more complex "things" starting from simpler ones. It goes hands in
hands with abstraction.
A |
B |
not B |
A and (not B) |
not A |
(not A) and B |
(A and (not B)) or ((not A) and B) |
False |
False |
True |
False |
True |
False |
False |
False |
True |
False |
False |
True |
True |
True |
True |
False |
True |
True |
False |
False |
True |
True |
True |
False |
False |
False |
False |
False |
And, if you look close enough, this behaves like an exclusive
or: either one, or the other, but not both!
What about that last row about assignment? This introduces the concept of variables, which we'll talk about next.