(c) 2018 Justin Bois. With the exception of pasted graphics, where the source is noted, this work is licensed under a Creative Commons Attribution License CC-BY 4.0. All code contained herein is licensed under an MIT license.
This document was prepared at Caltech with financial support from the Donna and Benjamin M. Rosen Bioengineering Center.
This lesson was generated from an Jupyter notebook. You can download the notebook here.
In this lesson, we will examine more operators beyond the arithmetic and assignment operators we have already encountered. We'll look at relational operators, identity operators, and logical operators. We'll use these operators in conditional statements, which help a program decide what to do in certain situations.
Suppose we want to compare the values of two numbers. We may want to know if they are equal for example. The operator used to test for equality is ==
, an example of a relational operator (also called a comparison operator).
Let's test out the ==
to see how it works.
5 == 5
5 == 4
Notice that using the operator gives either True
or False
. These are important keywords in Python that indicate truth. True
and False
have a special type, called bool
, short for Boolean.
type(True)
type(False)
The equality operator, like all relational operators in Python, also works with variables, testing for equality of their values.
a = 4
b = 5
c = 4
a == b
a == c
Now, let's try it out with some floats.
5.3 == 5.3
2.1 + 3.2 == 5.3
Yikes! Python is telling us that 2.1 + 3.2
is not 5.3
. This is floating point arithmetic haunting us. Note that some floating point numbers that can be exactly represented with binary numbers do not have this problem.
2.2 + 3.2 == 5.4
This behavior is unpredictable, so here is a rule.
As you might expect, there are other relational operators. The relational operators are
English | Python |
---|---|
is equal to | == |
is not equal to | != |
is greater than | > |
is less than | < |
is greater than or equal to | >= |
is less than or equal to | <= |
We can try some of them out!
4 < 5
5.7 <= 3
'steph curry' > 'lebron james'
Whoa. What happened on that last one? The Python interpreter clearly thinks Steph Curry is better than LeBron James, but that seems kind of subjective. Yes, I prefer Steph Curry's style of play, but there is no question that LeBron James is a basketball prodigy. To understand what the interpreter is doing, we need to understand how it compares strings.
In Python, characters are encoded with Unicode. This is a standardized library of characters from many languages around the world that contains over 100,000 characters. Each character has a unique number associated with it. We can access what number is assigned to a character using Python's built-in ord()
function.
ord('a')
ord('λ')
The relational operators on characters compare the values that the ord
function returns. So, using a relational operator on 'a'
and 'b'
means you are comparing ord('a')
and ord('b')
. When comparing strings, the interpreter first compares the first character of each string. If they are equal, it compares the second character, and so on. So, the reason that 'steph curry' > 'lebron james'
gives a value of True
is because ord('s') > ord('l')
.
Note that a result of this scheme is that testing for equality of strings means that all characters must be equal. This is the most common use case for relational operators with strings.
'lebron' == 'lebron james'
'lebron' == 'LeBron'
'LeBron James' == 'LeBron James'
'AGTCACAGTA' == 'AGTCACAGCA'
Python allow chaining of relational operators.
4 < 6 < 6.1 < 9.3
4 < 6.1 < 6 < 9.3
This is convenient do to. However, it is important not to do the following, even though it is legal.
4 < 6.1 > 5
In other words, do not mix the direction of the relational operators. You could run into trouble because, in this case, 5
and 4
are never compared. An expression with different relations among all three numbers also returns True
.
4 < 6.1 > 3
So, I issue a warning.
Identity operators check to see if two variables occupy the same space in memory; i.e., they are the same object (we'll learn more about objects as we go along in the bootcamp). This is different that the equality relational operator, ==
, which checks to see if two variables have the same value. The two identity operators are in the table below.
English | Python |
---|---|
is the same object | is |
is not the same object | is not |
That's right. The operators are pretty much the same as English! Let's see these operators in action and get at the difference between ==
and is
.
str_1 = 'Hello, world.'
str_2 = 'Hello, world.'
str_1 == str_2, str_1 is str_2
So, even though str_1
and str_2
have the same value, they do not occupy the same place in memory. Here are a few more examples.
str_1 = 'Hello, world.'
str_2 = str_1
str_1 == str_2, str_1 is str_2
a = 5.6
b = 5.6
a == b, a is b
a = 5.6
b = a
a == b, a is b
a = 5.6
b = a
a = 6.1
a == b, a is b
In the last two examples, we see that assigning b = a
, where a
is a float
in this case, means that a
and b
occupy the same memory. However, reassigning the value of a
resulted in the interpreter placing a
in a new space in memory. We can double check the values.
a, b
This automatic reassigning happens with immutable variables. This means that once the variables are created, their values cannot be changed. If we do change the value, as we did by setting a = 6.1
, the variable gets a new place in memory. All variables we've encountered so far, int
s, float
s, and str
s, are immutable. We will see encounter mutable data types in future lessons.
Logical operators can be used to connect relational and identity operators. Python has three logical operators.
Logic | Python |
---|---|
AND | and |
OR | or |
NOT | not |
The and
operator means that if both operands are True
, return True
. The or
operator gives True
if either of the operands are True
. Finally, the not
operator negates the logical result.
That might be as clear as mud to you. It is easier to learn this, as usual, by example.
True and True
True and False
True or False
True or True
not False and True
not(False and True)
not False or True
not (False or True)
7 == 7 or 7.6 == 9.1
7 == 7 and 7.6 == 9.1
I think these example will help you get the hang of it. Note that it is important to specify the ordering of your operations, particularly when using the not
operator.
Note also that
a < b < c
is equivalent to
(a < b) and (b < c)
With these new type of operators in hand, we can construct a more complete table of operator precedence.
precedence | operators |
---|---|
1 | ** |
2 | * , / , // , % |
3 | + , - |
4 | < , > , <= , >= |
5 | == , != |
6 | = , += , -= , *= , /= , **= , %= , //= |
7 | is , is not |
8 | and , or , not |
We have left out a few operators in Python. Two that we left out are the membership operators, in
and not in
, which we will visit in a forthcoming lesson. The others we left out are bitwise operators and operators on sets, which we will not be covering in the bootcamp.
As we move to conditionals, it is important to take a moment to evaluate the numerical values of the keywords True
and False
. They have numerical values of 1
and 0
, respectively.
True == 1
False == 0
You can do arithmetic on True
and False
, but you will get implicit type conversion.
True + False
type(True + False)
Conditionals are used to tell your computer to do a set of instructions depending on whether or not a Boolean is True
. In other words, we are telling the computer:
if something is true:
do task a
otherwise:
do task b
In fact, the syntax in Python is almost exactly the same. As an example, let's ask whether or not a codon is the canonical start codon (AUG
).
codon = 'AUG'
if codon == 'AUG':
print('This codon is the start codon.')
The syntax of the if
statement is apparent in the above example. The Boolean expression, codon == 'AUG'
, is called the condition. If it is True
, the indented statement below it is executed. This brings up a very important aspect of Python syntax.
Any lines with the same level of indentation will be evaluated together.
codon = 'AUG'
if codon == 'AUG':
print('This codon is the start codon.')
print('Same level of intentation, so still printed!')
What happens if our codon is not the start codon?
codon = 'AGG'
if codon == 'AUG':
print('This codon is the start codon.')
Nothing is printed. This is because we did not tell Python what to do if the Boolean expression codon == 'AUG'
evaluated False
. We can add that with an else
clause in the conditional.
codon = 'AGG'
if codon == 'AUG':
print('This codon is the start codon.')
else:
print('This codon is not the start codon.')
Great! Now, we have a construction that can choose which action to take depending on a value. So, if we're zooming along an RNA sequence, we could pick out the start codon and infer where translation would start. Now, what if we want to know if we hit a canonical stop codon (UAA
, UAG
, or UGA
)? We can nest the conditionals!
codon = 'UAG'
if codon == 'AUG':
print('This codon is the start codon.')
else:
if codon == 'UAA' or codon == 'UAG' or codon == 'UGA':
print('This codon is a stop codon.')
else:
print('This codon is neither a start nor stop codon.')
Notice that the indentation defines which clause the statement belongs to. E.g., the second if
statement is executed as part of the first else
clause.
While this nesting is very nice, we can be more concise by using an elif
clause.
codon = 'UGG'
if codon == 'AUG':
print('This codon is the start codon.')
elif codon == 'UAA' or codon == 'UAG' or codon == 'UGA':
print('This codon is a stop codon.')
else:
print('This codon is neither a start nor stop codon.')