Simple Operations
Learning Objectives
At the end of this sub-unit, students should
- understand the different simple operations on basic types in Python.
- know how to evaluate simple operations in Python.
- know how to write simple operations in Python.
Preliminary
Operations in Python numeric types (i.e., integer and floating point) follow the basic mathematical operations. But due to the limitations of typical keyboards, some symbols are changed to something similar. We assume you know basic mathematical operations including addition (i.e., \(x + y\)), subtraction (i.e., \(x - y\)), multiplication (i.e., \(x \times y\)), division (i.e., \(x \div y\)), and exponentiation (i.e., \(x^{y}\)).
You can also compose operations to form more complex operations. As with mathematics, there is order of precedence in evaluating the complex operations. But we are going to explain that only in later sub-units. For now, you should master simple operations.
Kinds of Operations
There are 3 basic kinds of operations available in Python. You can click on the "Kinds" to go directly to the subsection.
Kinds | Meaning | Types | Example |
---|---|---|---|
Arithmetic | Basic mathematics | Integer, Floating Point, and (partly) String | x + y , x - y , etc |
Relational | Operations to relate two values | Integer, Floating Point, and String | x < y , x >= y , etc |
Logical | Operations used in logic | Boolean | x and y , x or y , etc |
Arithmetic Operations
Numeric Type
For now, we will focus on numeric types for the meaning of arithmetic operations. We will then explain a potential reasoning on the extension to string type.
Arithmetic operations are the basic operations used in mathematics. This includes addition, subtraction, multiplication, division, and exponentiation. There is also an operation that is related to division but not commonly taught in high school namely modulo or remainder. Moreover, there is also a special kind of division operations in Python called floor division.
Arithmetic Operations
Group | Operation | Symbol | Mathematics | Example Math | Example Code | Result |
---|---|---|---|---|---|---|
Exponentiation | Exponentiation | ** |
\(x^y\) | \(3^2\) | 3 ** 2 |
9 |
Multiplicative Group |
Multiplication Division Floor Division Remainder |
* / // % |
\(x \times y\) \(x \div y\) \(\lfloor x \div y\rfloor\)1 \(x \ \% \ y\) |
\(7 \times 3\) \(7 \div 3\) \(\lfloor 7 \div 3\rfloor\)1 \(7 \ \% \ 3\) |
7 * 3 7 / 3 7 // 3 7 % 3 |
21 2.333 2 1 |
Additive Group |
Addition Subtraction |
+ - |
\(x + y\) \(x - y\) |
\(2 + 5\) \(2 - 5\) |
2 + 5 2 - 5 |
7 -3 |
You may notice the grouping of these operations. This is intentional and will be explained in a later sub-units on complex operations.
Let us start with terminology. Given an operation (e.g., \(x \times y\)), we say that:
- \(\times\) is the operator
- \(x\) is the left operand because it is on the left of operator (often shortened as LHS)
- \(y\) is the right operand because it is on the right of operator (often shortened as RHS)
Even in the case of exponentiation (i.e., \(x^{y}\)), we will still say that \(x\) is the LHS and \(y\) is the RHS. This conforms nicely to the example code. Additionally, only for exponentiation, we will also use the following terminology:
- \(x\) is the base
- \(y\) is the power (or exponent)
The symbols used in arithmetic operations looks like what you would expect with the exception of multiplication.
If you look at your keyboard, you will not find the symbol \(\times\).
Although it may seem like x
(i.e., X) looks similar to \(\times\), it is an alphabet and will be used for other things.
So, it cannot be used as a mathematical operators.
Instead, the symbol *
(i.e., Num *) is used.
You may notice from the exponentiation operations that the operands can be of different types. The same is true for multiplicative and additive group although we have no examples on that. For division (but not floor division), the result is always floating point. In the case that one of the operands is floating point while the other operand is integer, the result will always be a floating point number.
You may argue that for floor division, the result will always be integer. That is a good observation, but unfortunately that is not the case in Python. Since this is a bad practice, we will show such example only in the bad practice box. As for remainder operation, although it can be defined in the case where one or both of the operands are negative, we will consider them also as bad practice.
Arithmetic with Different Type
Remember that floating points are only an approximation of real numbers. The most famous example is shown below. Follow the link to better understand the reason why it behaves as it is.
Bad Practices
As usual in mathematics, we cannot divide by 0. Now, since remainder is closely related to division, we also cannot find a remainder of a value when divided by 0.
As mentioned also, floor division can also work for floating point number. The meaning is still the same, we perform division first, then we perform floor operation. Unfortunately, the resulting type is floating point number. Due to this, do not do this.
Remainder
We will have a little detour into remainder operation before looking at arithmetic operations for string.
First note that remainder operation %
is within the multiplicative group.
This is because it is closely related to both multiplication and division.
In the first place, division is often defined as an inverse of multiplication (i.e., if \(x \times y = z\), then \(y = z \div x\) and \(x = z \div y\) as long as \(x\) and \(y\) are non-zero).
Of course, in the realm of programming, we have to also think about the resulting type.
Now, multiplication for integer is also often defined as repeated addition. Since we can say that division is an inverse of multiplication, we also want to say that division is a repeated subtraction. This is merely an approximation because a number may not be fully divisible. If a number \(x\) is fully divisible by \(y\), we will find that if we repeatedly subtract \(y\) from \(x\), we will get 0.
But what happens if \(x\) is not fully divisible by \(y\)? Then we will get a remainder. In the case where \(x\) and \(y\) are both positive integer, this is the same remainder that we will get when we perform \(x \ \% \ y\). We will limit our explanation to when the operands are positive integers but the following formula works when the operands are negative.
Remainder Formula
You can simply memorize the formula, but let us dissect this formula a little bit for better understanding.
- \(\lfloor x \div y \rfloor\) is the floor division and finds how many times can we subtract \(y\) from \(x\).
- If we multiply the above by \(y\) again, we get \((\lfloor x \div y \rfloor \times y)\) which corresponds to the integer that can be fully divisible by \(y\).
- \(x - (\lfloor x \div y \rfloor \times y)\) is then the remainder, the integer that remains when \(x\) is not fully divisible by \(y\).
To complete our understanding of remainder, let us introduce another way to think about remainder. We will show this by looking at the result when we increase the value of \(x\) while keeping \(y\) constants. We will use \(y = 3\).
\(x\) | \(x \ \% \ 3\) | \(x\) | \(x \ \% \ 3\) | \(x\) | \(x \ \% \ 3\) |
---|---|---|---|---|---|
0 | 0 | 3 | 0 | 6 | 0 |
1 | 1 | 4 | 1 | 7 | 1 |
2 | 2 | 5 | 2 | 8 | 2 |
That is interesting. The result repeats between 0 to 2 (inclusive of both). In fact, this pattern continues as we increase the value of \(x\). Such pattern can actually be found on a clock. Consider a 12-hour clocks. 1 hour after 12:00 is not 13:00 but it returns back to 1:00. If we represent 12:00 as 00:00 instead, we will get the same pattern as the remainder when we divide by 12. As such, remainder can be used to represent values that are cyclic.
As a bonus, we get a nice property when a number \(x\) is fully divisible by \(y\).
Fully Divisible
A number \(x\) is fully divisible by \(y\) if the remainder when \(x\) is divided by \(y\) (i.e., \(x \ \% \ y\)) is exactly 0.
In the analogy with cyclic values, we can see that for remainder when divided by \(x\), we get back to the remainder of 0 every \(x\) number of steps.
Visual Representation
A visual representation of a remainder is like an \(n\)-sided regular polygon. We will then label the corner of the polygon with numbers starting from 0 at the top. Since we will be dealing with normal cases of positive operands, the label will go in a clockwise direction, increasing the label by 1 from one corner to the next. This forms exactly \(n - 1\) labels for \(n\)-sided regular polygon.
Then k % n
is an operation of moving clockwise from label 0 for \(k\) number of steps.
The label we land on is the result of the operation.
Example of the construction of polygon for \(n = 3\) and \(n = 5\) are shown below.
We also show how to solve for 7 % 3
and 8 % 5
.
This is represented by the arrows in the clockwise direction.
Bad Practices
As mentioned, remainder operation can work for negative values. We will not explain the result as you should not do this, but you can use the formula to get the results shown below. Additionally, do not use remainder operation for floating point operands.
Surprisingly, the formula we obtained that relates floor division and remainder operation is still valid even in the case of floating point operands. This is, of course, still a bad practice but we simply want to show the generality of the formula.
The computation goes as follows:
7.0 % 3.1
= 7.0 - (\(\lfloor\)7.0 \(\div\) 3.1\(\rfloor\) * 3.1)
= 7.0 - (\(\lfloor\)2.258064516129032\(\rfloor\) * 3.1)
= 7.0 - (2 * 3.1)
= 7.0 - 6.2
= 0.8
The difference is due to imprecision in floating point number. Visualization of remainder operation with negative operands can be captured by the following image.
String Type
In the case of string, only some of arithmetic operations are available.
As such, we will call this arithmetic-like operations.
The two operations that are available are +
and *
.
For historical reason, +
is the string concatenation.
Unfortunately, there is no direct mathematical equivalence for string operations.
Arithmetic-Like Operations
Operation | Symbol | Example Code | Result | Comment |
---|---|---|---|---|
Repetition | * |
3 * 'ABC' |
'ABCABCABC' |
One of the operand must be an integer |
Concatenation | + |
'AB' + 'CD' |
'ABCD' |
Both operands must be strings |
Let's start with +
as concatenation.
Since it shares symbol with addition, we can call this operation as a "plus" operation.
Now, consider what *
is for integer.
We typically think of \(x \times y\) as \(y + y + \cdots + y\)2.
In other words, it is a repeated "plus".
We can carry this analogy into the realm of string type.
Since *
is a repeated "plus" and "plus" is really just +
, we can then define 3 * 'ABC'
as 3-times of 'ABC'
.
So, it becomes 'ABC' + 'ABC' + 'ABC'
.
This is then equivalent to 'ABCABCABC'
.
So now, we have defined *
in terms of +
.
This is one way to remember what *
is in Python.
As stated above, in the case of *
, one of the operand must be an integer.
This can be either LHS or RHS.
The other operand must be a string for this operation to be a repetition.
However, do note that *
is also defined for numeric types, so we can also have both operands being integers.
What we cannot have, on the other hand, is both operands being strings.
This is a concept that is common in programming called overloading.
We say that the operator *
is overloaded and the behavior depends on the type supplied as the operands.
In the case of +
, both operands must be string for the operation to be concatenation.
We can also say that the operator +
is overloaded as it also works when both operands are integer.
Arithmetic-Like Operations
Bad Practices
When the data type does not make sense, you will get an error.
To prevent the error on the last example, we can convert 3 to string by using str(3)
.
Visualization
It is important to note that concatenation and repetition will always produce new strings. Conceptually, if we put each character in a box, then we will get new box when we perform concatenation.
Relational Operations
Relational operations relates two values.
The answer to the relation operations are boolean values.
True
corresponds to the relation being correct and False
corresponds to the relation being incorrect.
Similar to before, we will discuss relations on numeric type before discussing relations on string type.
Numeric Type
Relational Operations
Operation | Symbol | Mathematics | Code | Result |
---|---|---|---|---|
Equal to Not Equal to Less Than or Equal to Less Than Greater Than or Equal to Greater Than |
== != <= < >= > |
\(x = y\) \(x \neq y\) \(x \leq y\) \(x < y\) \(x \geq y\) \(x > y\) |
3 == 2 3 != 2 3 <= 2 3 < 2 3 >= 2 3 > 2 |
False True False False True True |
The first thing you notice is probably the lack of grouping. This, again, will be explained in later sub-units. The second thing you notice is probably the difference between the usual mathematical operators and the symbols used in Python.
Take for instance, the equal to operator.
Instead of just a single equal sign (i.e. =
), we use two equal signs without any space in-between (i.e. ==
).
The reason for this is that because the symbol =
is already taken.
But we will only introduce that later.
As for the operators !=
, <=
, and >=
, the reason is because the corresponding mathematical symbols (i.e., \(\neq\), \(\leq\), and \(\geq\) respectively) are not available on a typical keyboard.
So the alternative is to use more symbols.
It is important to note that for the symbols involving two characters, you should not have whitespace (i.e., Space) in between.
We will omit specific examples for each operator as the meaning is hopefully straightforward. Instead, what we will show is that Python is smart enough to compare two different numeric types. In other words, you can compare integer with floating point numbers.
Numeric Relational Operations
Try it out with other numbers. You will find that they work as how you would expect.
So that shows how powerful Python relational operator is. To make it even more powerful, we will introduce you to the concept of composition. Composition is a capability to combine different concepts in programming languages to extend its capability. What we have learnt so far are arithmetic and relational operations. Can we combine them? YES!
Arithmetic + Relational Operations
Bad Practices
Due to imprecision, you should not compare two floating point numbers using ==
(i.e., equal to) or !=
(i.e., not equal to) especially if you combine with arithmetic operations.
- Remember,
0.1 + 0.2
is0.30000000000000004
String Type
Unline arithmetic, all the relational operations are also available for string. In the case of equal to and not equal to, the behavior is rather straightforward. The comparison is like what you expect that all symbols in LHS must be the same as all symbols in RHS. The symbols must also appear in the same position.
What you need to note is that the comparison is case-sensitive.
In other words, uppercase A
is different from lowercase a
.
String Relational Equality
Now let us look at other relational operations for string.
This is where things get slightly more complicated.
Let us simplify it a little first by considering only strings of a single character.
In other words, we want to be able to compare character3.
The question is, how do we compare 'A'
and 'a'
?
What about '0'
and '!'
?
There must be a convention. The formalization of the convention can be done using a table/lexicon. This table is a special table called the ASCII table. The comparison is then called lexicographical ordering from the lexicon that we use.
You do not have to memorize the ASCII table. Typically, the relevant group ordering is as summarized below.
Group | Characters | Range |
---|---|---|
Numeric | 0123456789 | 0-9 |
Uppercase | ABCDEFGHIJKLMNOPQRSTUVWXYZ | A-Z |
Lowercase | abcdefghijklmnopqrstuvwxyz | a-z |
As you can see, all numeric characters are considered smaller than all uppercase characters. Similarly, all uppercase characters are smaller than all lowercase characters. In other words: numeric \(<\) uppercase \(<\) lowercase.
What we have left now is to compare strings consisting of multiple characters.
To say that a string \(s_1\) is less than another string \(s_2\), we still need to try to compare all characters in the same position.
So let us explain more about how we can visualize a string.
We will use 'ABC'
as an example.
String Visualization
We can represent a string by putting each symbol into a box.
So for the string 'ABC'
, we will have the image below.
Given a visualization, we can read the string by looking at the symbols from left to right.
Then we simply enclose it within the appropriate quotation mark.
So the above string visualization indeed becomes 'ABC'
.
Given the string visualization above, we can then compare two string by placing them one above another. To make it work, the leftmost symbol must be aligned. We can then compare the string character by character from left to right.
String Comparison
First, consider the case of the two string having the same number of characters.
Also, let us restrict the comparison to either <
and >
.
Here is the explanation for the image above.
We compare character by character from left to right using the given operator (e.g., <
).
We will only compare characters in the same position (i.e., the same index).
During the comparison process, we have 3 possibilities.
- The two characters are equal.
- We continue the comparison with the next character.
- If there is no more character to compare, then the result is
False
.
- The character at the top is less than the character at the bottom.
- We can conclude that the top character is indeed less than the character at the bottom so we return
True
.
- We can conclude that the top character is indeed less than the character at the bottom so we return
- The character at the top is greater than the character at the bottom.
- We can conclude that the top character is actually greater than the character at the bottom so we return
False
.
- We can conclude that the top character is actually greater than the character at the bottom so we return
Obviously, if all the characters are equal, then both <
and >
will return False
as we can see from the last two examples above.
One of the benefit of programming is that we can immediately check our result.
Let's see how it is on Python.
Success!
To extend this to string with different number of characters, we will simply have a convention that if we were to compare any character with no character, the latter (i.e., no character) is considered smaller than any character.
Great Success!
Finally, to extend this to <=
and >=
, we simply check if the above gives us True
or the two strings are equal.
We already know to check equality.
Do try it out on Python and observe the behavior.
Full Comparison
Believe it or not, once we have one of the comparison (e.g., <=
), we can define the rest of the comparisons as follows:
Operation | From <= |
Note |
---|---|---|
s1 >= s2 |
s2 <= s1 |
Simply swap the two operands |
s1 == s2 |
s1 <= s2 and s1 >= s2 |
We have already defined >= |
s1 != s2 |
not (s2 == s1) |
We have already defined == |
s1 < s2 |
s1 <= s2 and s2 != s1 |
We have already defined != |
s1 > s2 |
s2 < s1 |
We have already defined > |
Can you try rewriting all using only <=
?
Can you also define all using only <
?
Common Mistakes
One common mistake is to compare string and numeric types.
This will only work for equal to and not equal to.
The result is simply False
because a string is not equal to a number, even when both consists of the same symbols when we look at the code.
For other relational operations, you will instead get an error.
Even when we are comparing two strings, there is still a common mistake of thinking in terms of numeric value as well as confusion on uppercase vs lowercase. Recap, the behavior when comparing two string is comparing character by character. This is done regardless of what the character is.
Logical Operations
Logical operations are used to construct logical statements and operates (primarily) on boolean values. As a warning, the operations also work on other values BUT the result is probably not what you would expect. You should avoid using logical operations on other types beside boolean as more often than not, these are bad practices.
Logical Operations
Operation | Symbol | Example Code | Result |
---|---|---|---|
Negation | not |
not True |
False |
Conjunction | and |
True and False |
False |
Disjunction | or |
False or True |
True |
If you are not familiar with logics in mathematics, the operations can be captured as truth table. This is because there are only 2 possible boolean values so we can easily enumerate them. The truth table can be found below. Click open the tab for the truth table for the different operations.
\(x\) | not \(x\) |
---|---|
True |
False |
False |
True |
This is the simplest operation.
We will simply negate the boolean values.
So, "correct" (i.e., True
) becomes "incorrect" (i.e., False
) and "incorrect" (i.e., False
) becomes "correct" (i.e., True
).
\(x\) | \(y\) | \(x\) and \(y\) |
---|---|---|
True |
True |
True |
True |
False |
False |
False |
True |
False |
False |
False |
False |
One way to remember this is that \(x\) and
\(y\) evaluates to True
only when exactly both of LHS and RHS are True
.
Otherwise, the result is False
.
\(x\) | \(y\) | \(x\) or \(y\) |
---|---|---|
True |
True |
True |
True |
False |
True |
False |
True |
True |
False |
False |
False |
One way to remember this is that \(x\) or
\(y\) evaluates to True
when one or both of LHS and RHS are True
.
Otherwise, the result is False
.
Bad Practices
Since this is pretty common, we do not hide this bad practice so you are exposed to it and understand. Still, you should not write your code like this.
Although Python allows using logical operators for non-boolean values, the result is not what you would expect. The explanation is closely related to Conversion to Boolean. There is a name for this.
- Falsy Values: Values that when converted to boolean becomes
False
. - Truthy Values: Values that when converted to boolean becomes
True
.
The easy way to remember this is shown below.
Truthy/Falsy
Values corresponding to empty values are falsy values. All other values are truthy values.
Given that, we can give a more condensed truth table as follows.
Bsed on this condensed truth table, if we were to evaluate -1 or True
, we look at the condensed table for disjunction and check the value of LHS.
-1 is non-empty, so it is truthy.
Therefore, the result should be \(x\) --or in this case-- -1
.
Summary
Summary of Operations
The following summary is only for best practices. We will not provide a summary for bad practices as we expect you to avoid them. In particular, no summary is provided for the behavior of the operation with badly typed data even when the operation produces a value in Python.
Symbol | Numeric | String | Boolean |
---|---|---|---|
** |
Exponentiation | - | - |
* / // % |
Multiplication Division Floor Division Remainder |
Repetition - - - |
- - - - |
+ - |
Addition Subtraction |
Concatenation - |
- - |
not |
- | - | Negation |
and |
- | - | Conjunction |
or |
- | - | Disjunction |
Review
Question 1
What is the result of 10 + 10
?
Question 2
What is the result of 10 + 10.0
?
Question 3
What is the result of '10' + 10
?
Question 4
What is the result of '10' + '10'
?
Question 5
What is the result of '10' * 10
?
Question 6
Consider LHS + RHS
. What are the possible pair of types for LHS and RHS?
Question 7
Let s1
be "ACB"
and s2
be "aBC"
. Which of the following returns True
?
-
The symbol \(\lfloor x \rfloor\) is the floor operation. It finds the largest integer smaller than or equal to \(x\). In other words, if \(x\) is already an integer, the result is simply \(x\). ↩↩
-
This definition is applicable only for integer. We follow the typical convention of \(x\)-times of \(y\), but as we know, addition is commutation for integer. So it is equivalent to \(y\)-times of \(x\). ↩
-
Note that unlike other programming languages like C or Java, there is no such thing as "character" in Python. In Python, a single character is still a string. ↩
-
This behavior is also called "short-circuit" evaluation. Because we need not evaluate the RHS (i.e., \(y\)) if the LHS operand is already fixing the result. ↩↩