C6. Taking decisions using conditionals and more about loops#

Conditionals#

Till now, our codes have a linear flow: the statements are executed from the start to the end, even within loops. Now, we will learn about conditionals and how they can break this linearity.

A computer can store information, using zeros and ones, but they also have a robust logic system based on boolean algebra. As a result, they can perform complex tasks; even beating the world champion of chess. This happens because they can take decisions. That is, for performing complex tasks, computers need to break the linearity within the code:

Conditionals are statements (commands) used in our programs to make decisions. They are able to control the flow of our code

Boolean data type#

Boolean variables can have two different values:

  • True

  • False

The logic of computers is based on this#

Because of the way they are built (electronics): voltage on/off, 1/0, True/False

# True or False
# Be careful, the first letter is uppercase
# The next is not valid: true, false
print(True)  # and no quotes: a boolean not a str
print(False)
True
False
# a variable
boolean_var = True
print(boolean_var)
True

Relational operators#

They are used to evaluate

Obtaining a boolean variable: True or False

Symbol

Evaluation

<

less than

<=

less than or equal to

>

greater than

>=

greater than or equal to

==

equal to

!=

not equal to

is

object identity

is not

negated object identity

Note that “==” has nothing to do with “=”

Examples#

Think on each result and code to see if you are correct. Also try your own examples

5 < 3
5 > 3
7 > 13
0.1 <= 1
5 == 55
3 != 4
# For the sake of an easy reading
print('5 < 3:',    5 < 3)
print('5 > 3:',    5 > 3)
print('7 > 13:',   7 > 13)
print('0.1 <= 1:',   0.1 <= 1)
print('5 == 55:',    5 == 55)
print('3 != 4:',   3 != 4)
5 < 3: False
5 > 3: True
7 > 13: False
0.1 <= 1: True
5 == 55: False
3 != 4: True

More complex examples using functions and methods#

Try the next examples

len("ATCG") == 4
len("ATCGA") != 5
"CAGCAGCAGG".count('CAG') == 3 
"CAGCAGCAGG".count('G') != 4
# For the sake of clarity, but type it. Do not cp and paste
print('len("ATCG") == 4:',               len("ATCG") == 4)
print('len("ATCGA") != 5:',              len("ATCGA") != 5)
print('"CAGCAGCAGG".count("CAG") == 3:', "CAGCAGCAGG".count('CAG') == 3)
print('"CAGCAGCAGG".count("G") != 4',    "CAGCAGCAGG".count('G') != 4)
len("ATCG") == 4: True
len("ATCGA") != 5: False
"CAGCAGCAGG".count("CAG") == 3: True
"CAGCAGCAGG".count("G") != 4 False

New str methods that returns a boolean#

True or False

"ATCG".startswith("ATG") 
"ATCG".startswith("DR")
"ATCGATAG".endswith("TAG")
"ATCGATAG".endswith("JR")
"ATCGATAG".isupper()
"ATCGATAG".ilower()
# For the sake of clarity:
print('"ATGCG".startswith("ATG"):',  "ATGCG".startswith("ATG"))
print('"ATGCG".startswith("DR"):',   "ATCG".startswith("DR"))
print('"ATCGATAG".endswith("TAG"):', "ATCGATAG".endswith("TAG"))
print('"ATCGATAG".endswith("JR"):',  "ATCGATAG".endswith("JR"))
print('"ATCGATAG".isupper():',       "ATCGATAG".isupper())
print('"ATCGATAG".islower():',       "ATCGATAG".islower())
"ATGCG".startswith("ATG"): True
"ATGCG".startswith("DR"): False
"ATCGATAG".endswith("TAG"): True
"ATCGATAG".endswith("JR"): False
"ATCGATAG".isupper(): True
"ATCGATAG".islower(): False
# You should always try more examples, like
print('"ATcgATAG".isupper():',       "ATcgATAG".isupper())
print('"ATCGAtAG".islower():',       "ATCaGAgAG".islower())
"ATcgATAG".isupper(): False
"ATCGAtAG".islower(): False

Membership test operators#

in and not in

Membership test operations
**************************

The operators "in" and "not in" test for membership.  
"x in s" evaluates to "True" if *x* is a member of *s*, 
and "False" otherwise "x not in s" returns the negation of "x in s".  
# not in: against the same list
print('M' not in ['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y'])
False
# not in: against the same list
print('M' not in ['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y'])
False
# not here
print('X' in ['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y'])
False
# check in a str 
"c" in "ATCG"
False
# Now it is here
"C" in "ATCG"
True

Note: do not confuse with the in in the loop (for)

    for x in y:
        body of the for

if: building a conditional statement#

Makes a decision

if condition then
# See an example:
aa = "M"
if aa != "V": # if condition then
    print(aa, "is not Valine")
M is not Valine

Parts of an if statement#

* First line of the if:
    - **if**
    - __condition__
    - **:**  
*  Body of the if:
    - **block of indented lines**
# Example:
for aa in ['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y']:
    if aa in ['V','I','L','M','F','W','C']: # A more complicated condition
        print(aa, "is hydrophobic")  # this line is double indexed (for and if)
C is hydrophobic
F is hydrophobic
I is hydrophobic
L is hydrophobic
M is hydrophobic
V is hydrophobic
W is hydrophobic

continue, break and pass#

in combination with if. They help to build very useful statements within loops (ie. for)

continue#
# continue: jumps to the next iteration
aas1 = ['A','C','D','E','F']
aas2 = ['G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y']
for aa in aas1 + aas2: # + concatenates the two lists
    if aa in aas2: # but 5 first elems
        continue
    print(aa, "reached")
print("...Out of the loop")
A reached
C reached
D reached
E reached
F reached
...Out of the loop
break#
# break: jumps out of the loop
aas = ['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y']
for aa in aas:
    if aa == 'M': 
        break
    print(aa, "reached")
print("...Out of the loop")
A reached
C reached
D reached
E reached
F reached
G reached
H reached
I reached
K reached
L reached
...Out of the loop
pass#
# pass: is a null statement. 
# It means: still I do not know which code should be writen there
aas1 = ['A','C','D','E','F']
aas2 = ['G','H','I','K','L','M','N']
aas3 = ['P','Q','R','S','T','V','W','Y']
for aa in aas1 + aas2 + aas3:
    if aa in aas1 + aas3: 
        pass # placeholder: It will be written later. At the moment do nothing
    else:
        print(aa, "reached")
print("...Out of the loop")
G reached
H reached
I reached
K reached
L reached
M reached
N reached
...Out of the loop

else#

Means otherwise and makes a decision:

if condition:
    body_of_if
else: 
    body_of_else
# classifying amino_acids because of chemical properties
aa_list = ['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y'] # 20 aa
sulphur_containing_list = ['M', 'C'] # Methione, Cysteine
for aa in aa_list:
    if aa in sulphur_containing_list: # 
        print(aa, "contains sulphur")
    else: # Otherwise: if it is NOT in the 1st list
        print(aa, "does NOT contain sulphur")
A does NOT contain sulphur
C contains sulphur
D does NOT contain sulphur
E does NOT contain sulphur
F does NOT contain sulphur
G does NOT contain sulphur
H does NOT contain sulphur
I does NOT contain sulphur
K does NOT contain sulphur
L does NOT contain sulphur
M contains sulphur
N does NOT contain sulphur
P does NOT contain sulphur
Q does NOT contain sulphur
R does NOT contain sulphur
S does NOT contain sulphur
T does NOT contain sulphur
V does NOT contain sulphur
W does NOT contain sulphur
Y does NOT contain sulphur

We can use else to divide in more than 2 sets. See the next example with 3 sets:

# We divide the digits in 3 sets
digit_list = list(range(0, 10, 1)) # a list of digits
for digit in digit_list:
    if digit <= 3:
        print(digit, "is <= 3")
    else:
        if digit <= 7:
            print(digit, "is >3 and <=7")
        else:
            print(digit, "is >7")
0 is <= 3
1 is <= 3
2 is <= 3
3 is <= 3
4 is >3 and <=7
5 is >3 and <=7
6 is >3 and <=7
7 is >3 and <=7
8 is >7
9 is >7

elif#

Combination of else and if. Also makes a decision:

if condition_1:
    body_of_if
elif condition 2: # (not condition_1) and condition_2
    body_of_elif   
else: # otherwise  => (not condition_1) and (not condition_2)    
    body_of_else

The previous example

# We divide the digits in 3 sets
digit_list = list(range(0, 10, 1)) # a list of digits
for digit in digit_list:
    if digit <= 3:
        print(digit, "is <= 3")
    elif digit <= 7:
        print(digit, "is >3 and <=7")
    else:
        print(digit, "is >7")
0 is <= 3
1 is <= 3
2 is <= 3
3 is <= 3
4 is >3 and <=7
5 is >3 and <=7
6 is >3 and <=7
7 is >3 and <=7
8 is >7
9 is >7

We can even use “elif” to classify in more sets. For instance in 5 sets:

# We divide the digits in 5 sets
digit_list = list(range(0, 10,1)) # a list of digits
for digit in digit_list:
    if digit <= 1:
        print(digit, "is <= 1")
    elif digit <= 3:
        print(digit, "is >1 and <=3")
    elif digit <= 5:
        print(digit, "is >3 and <=5")     
    elif digit <= 7:
        print(digit, "is >5 and <=7")    
    else:
        print(digit, "is >7")
0 is <= 1
1 is <= 1
2 is >1 and <=3
3 is >1 and <=3
4 is >3 and <=5
5 is >3 and <=5
6 is >5 and <=7
7 is >5 and <=7
8 is >7
9 is >7

Nesting “if”, “elif” and “else”#

We can nest “if”, “elif” and “else” to classify complex sets

Human Population History Duda, P., Jan Zrzavý Human population history revealed by a supertree approach. Sci Rep 6, 29890 (2016)

Functions that return a boolean type: True or False#

Their outputs can be used in conditionals

def is_GC_rich(dna):
    GC_content = (dna.count('G') + dna.count('C')) / len(dna)
    if GC_content > 0.65:
        return True
    else:
        return False
    
# main
print(is_GC_rich("ATCGCGCGACCGG"))
print(is_GC_rich("ATCTCGCGATTCCGCT"))
True
False

Booleans#

Boolean operators: and, or, not#

This is very important because more complex conditions can be build

and#
accs = ['ab56', 'bh84', 'hv76', 'ay93', 'ap97', 'bd72']
for accession in accs:
    if accession.startswith('a') and accession.endswith('3'):
        print(accession)
ay93
or#
accs = ['ab56', 'bh84', 'hv76', 'ay93', 'ap97', 'bd72']
for accession in accs:
    if accession.startswith('a') or accession.startswith('b'):
        print(accession)
ab56
bh84
ay93
ap97
bd72
not#
accs = ['ab56', 'bh84', 'hv76', 'ay93', 'ap97', 'bd72']
for accession in accs:
    if not accession.startswith('a'):
        print(accession)
bh84
hv76
bd72
operator precedence#

not, and, or: from highest to lower precedence

# example of precedence: 1.not, 2.and
accs = ['ab56', 'bh84', 'hv76', 'ay93', 'ap97', 'bd72']
for accession in accs:
    if not accession.startswith('a') and accession.startswith('b'):
        print(accession)
bh84
bd72
# example of precedence: 1.not, 2.and
accs = ['ab56', 'bh84', 'hv76', 'ay93', 'ap97', 'bd72']
for accession in accs:
    if not accession.startswith('a') or accession.startswith('b'):
        print(accession)
bh84
hv76
bd72
# example of precedence: 1.and, 2.or
accs = ['ab56', 'bh84', 'hv76', 'ay93', 'ap97', 'bd72']
for accession in accs:
    if accession.startswith('a') or accession.startswith('b') and accession.startswith('b'):
        print(accession)
ab56
bh84
ay93
ap97
bd72
Combining the boolean operators: and, or, not#

to build any logic expression (boolean algebra)

Loops#

while#

It loops while a condition is True

while condition:
    indented_body_of_while
# example of while
digit = 0
while digit < 10:
    print(digit)
    digit += 1  # digit = digit + 1
0
1
2
3
4
5
6
7
8
9
# The last while is equivalent to:
for digit in range(0,10):
    print(digit)
0
1
2
3
4
5
6
7
8
9

Summary#

  • Conditional:

    • Boolean variable type:

      • True

      • False

    • Relational operators (==, !=, >, …)

    • str.methods returning a boolean

    • Membership (in, not in)

    • if, else, elif

    • boolean operators:

      • and

      • or

      • not

  • Loops:

    • while


Exercises#