Lesson A6 – Conditions

In this part of the tutorial we learn about what is True and False in Python. Testing if a certain condition is fulfilled, is a vital element of programming.

Emtpy type

In the context of conditions and testing them, it is also nice to have a representation for an absent value in the sense of an “unset” or “no-value” value. In Python there is a specific data type for an empty value, which is called None.

[1]:
# None
n = None
print(type(n))
<class 'NoneType'>

None is not nothing but a full-fledged exiting object. None can be passed to and returned by functions.

Boolean values

A boolean variable is a data type which can assume only one of two values: True or False.

[2]:
# boolean
b = True
print(type(b))
<class 'bool'>

Other data types can be converted into boolean data types with the bool() function, e.g. the integer 1 is converted to True while the integer 0 is converted to False. You can even convert strings to booleans. In fact every defined, non-empty object evaluates to True. None as an empty-value evaluates to False.

[3]:
print(bool(1))
print(bool(0))
print(bool("False"))
print(bool(None))
True
False
True
False

Advanced

Because of this relation we can also kind of calculate with boolean values.

[4]:
print(True * False)
print(True + True)
print(True + True * False)
0
2
1

Empty collections evaluate to False as well.

[5]:
list_empty = []
print(bool(list_empty))
False

Comparison operators

True and False play an important role, when you want to test for certain conditions. You can compare things with the bit-wise operators summarised in the table below.

Operator

Meaning

a == b

True, if a equal to b

a != b

True, if a unequal to b

a < b

True, if a less than b

a > b

True, if a greater than b

a <= b

True, if a less than or equal to b

a >= b

True, if a greater than or equal to b

a & b

True, if a AND b are True

a | b

True, if a AND/OR b are True

a ^ b

True, if a XOR b are True

Note, that the “equal”-operator is == instead of just = since a single equal sign is already used for variable assignments.

[6]:
print(1 == 1)  # equal?
print(1 != 1)  # not equal?
True
False
[7]:
print("True?", 2 > 1)
# 2 greater than 1?

print("True AND True?", (1 < 2) & (2 > 1))
# 1 less than 2 AND 2 greater than 1?

print("True AND True?", 1 < 2 > 1)
# 1 less than 2 AND 2 greater than 1? (Python short hand notation)

print("True AND/OR True?", (1 > 2) | (2 > 1))
# 1 greater than 2 AND/OR 2 greater than 1? (At least one statement True)

print("True AND/OR True?", (1 < 2) | (2 > 1))
# 1 less than 2 AND/OR 2 greater than 1? (At least one statement True)

print("True XOR True?", (1 > 2) ^ (2 > 1))
# 1 greater than 2 OR 2 greater than 1? (Only one statement True)

print("True XOR True?", (1 < 2) ^ (2 > 1))
# 1 less than 2 OR 2 greater than 1? (Only one statement True)
True? True
True AND True? True
True AND True? True
True AND/OR True? True
True AND/OR True? True
True XOR True? True
True XOR True? False

These bit-wise operators have their literal expressions summarised in the table below which can be used instead. But be aware, that they do not always have the same meaning (see subsection Advanced below).

Operator

Meaning

a is b

True, if a is identical to b

a is not b

True, if a is not identical to b

a and b

True, if a AND b are True

a or b

True, if a AND/OR b are True

[8]:
print("True AND True?", (1 < 2) and (2 > 1))
print("True OR True?", (1 > 2) or (2 > 1))
True AND True? True
True OR True? True

Advanced

The is operator

The literal is operator has a different meaning than the == operator.

[9]:
a = 1
b = 1
print(a is b)  # identical?
print(a is not b)  # not identical?
True
False

But …

[10]:
a = 450
b = 450
print(a is b)  # equal?
print(a is not b)  # not equal?
False
True

While == tests for value equality, is tests for object identity. Due to the way Python handles the storage of integer values for example, a stored 1 is always the same object, which is true for a few other integers, too. A stored 450 on the other hand is only equal to another 450 by value but it is not the same object.

Note, while bool(None) evaluates to False, None is neither equal to False, nor is it the same object.

[11]:
print(None is None)
print(True is True)
print(False is False)
print(None is False)
print(None is True)
True
True
True
False
False
[12]:
print(None == None)
print(True == True)
print(False == False)
print(None == False)
print(None == True)
True
True
True
False
False

On the other hand, True and False are equal to 1 and 0 by value (but are different object of course).

[13]:
print(True == 1)
print(False == 0)
True
True
[14]:
print(True is 1)
print(False is 0)
False
False

The and and or operator

The and and or operator may also not work as you expect so be careful:

[15]:
print(1 and 2)
print(2 and 1)
# If True return the last True value (not True)
2
1
[16]:
print(1 and 0)
print(0 and 1)
print(None and 0)
# If False return the first False value (not False)
0
0
None
[17]:
print(1 or 2)
print(2 or 1)
# If True return the first True value (not True)
1
2
[18]:
print(None or 0)
print(0 or None)
# If False return the last value (not True)
0
None

The in operator

In Python there is additionally a literal comparison operator stating if an object is part of another one: the in operator. It can be for example used to discover if an object is contained in a container.

[19]:
1 in [1]
[19]:
True

If and else

So for now we can write straightforward programs as a series of statements that are executed one after the other. We get more flexibility in our code, when we test for conditions and do different things, depending on the outcome of the test. We can do this in Python using the if directive. Note, that the code block following the statement with the if directive is indented by 4 spaces. In this way Python knows, that this code block is subordinate and only to be executed if the if statement evaluates to True.

[20]:
a = 1
if a == 1:  # <- colon after the conditional statement
    print("a is really equal to one")
a is really equal to one

Note: We had our first encounter here with something very essential to Python: indention. So far we used indention only for cosmetic reasons. Now that we use a directive that introduces some hierarchy of statements in our code, we need to make sure that the subordinate statements are indented. You need the same indention policy throughout your whole program. By convention indention is done by 4 spaces per hierarchy level.

We can extend this with the elsedirective.

[21]:
a = "Hello, World!"

# Test if the first character of the string `a` is not equal to "X"
if a[0] != "X":
    print(a)
else:
    print("I cannot work with this ...")
Hello, World!

We can test also for several conditions, one by one with the elif directive.

[22]:
a = ["aspirin", "ethanol"]

# Test if the string "aspirin" is an element of the list `a`
if "aspirin" in a:
    print("I can use this")

# Otherwise test if the string "ethonal" is an element of the list `a`
elif "ethanol" in a:
    print("Ok, than I will use this")

# Otherwise ...
else:
    print("I cannot work with this ...")
I can use this

Advanced

Try and except

Using if and else to test if something is True and than act accordingly seems like a reasonable procedure to write programs that can deal with varying situations. With this “asking for permission first before you do something”-approach, you are often on the safe side. It is typical to Python that it also allows you to do it the other way round. In many cases, it can be better just to try something you want to do, and only act accordingly if something goes wrong. This is particularly nice if you are checking a condition that is true most of the time and leads to an exception if it is not. The magic words here are try and except.

[23]:
a = [1, 2, 3]

# Try to append a value to the list `a`
try:
    a.append(4)

# And if this does not work ... (but it does!)
except AttributeError():
    pass  # Do nothing

print(a)
[1, 2, 3, 4]
[24]:
a = (1, 2, 3)

# Try to append a value to the tuple `a`
try:
    a.append(4)

# And if this does not work ... (and it doesn't!)
except AttributeError:
    print("I cannot work with this ...")

print(a)
I cannot work with this ...
(1, 2, 3)

You can additionally use the else and the finally directive to extend this.

[25]:
a = [1, 2, 3]

try:
    # Let's see if this works ...
    a.append(4)
except AttributeError:
    # Aha, it did not ...
    print("I cannot work with this ...")
else:
    # Yes, it worked!
    print("Appended 4 to a")
finally:
    # No matter if it worked ...
    print(a)
Appended 4 to a
[1, 2, 3, 4]

This can seem a bit more complex at first compared to the if-else variant because you need to anticipate which kind of error would be raised, if what you try to do fails. But depending on the individual case, the try-except variant can be also easier to understand. In any case it is a good thing that we can tackle conditional program execution from different angles. The if-else code of the last example could look like this by the way:

[26]:
a = [1, 2, 3]
# Let's see if this works ...
if isinstance(a, list):
    # Yes, it will work!
    a.append(4)
    print("Appended 4 to a")
else:
    # Aha, it will not ...
    print("I cannot work with this ...")

# No matter if it worked ...
print(a)
Appended 4 to a
[1, 2, 3, 4]

Note: The isinstance(object, type) function is the preferred way of saying type(object) is type.