Level 1 · First Words

True, False and Questions ⚖️

Before a program can decide anything, it has to ask a question that has exactly two possible answers. Here is how Python asks.

A third kind of value

sunny = True
raining = False

print(sunny, type(sunny))
print(raining)
True <class 'bool'>
False

Capital T, capital F, no quotes. "True" with quotes is a piece of text and a completely different thing. The type is named bool after George Boole, a Victorian mathematician who worked out the algebra of true and false about ninety years before there was a computer to run it on.

Comparisons produce booleans

print(5 > 3)
print(5 < 3)
print(5 == 5)      # equal? two equals signs!
print(5 != 5)      # not equal
print(5 >= 5)
print("a" < "b")   # alphabetical order
True
False
True
False
True
True
🪤 One equals sign or two

= assigns a value. == asks a question. Writing if x = 5 is a SyntaxError in Python, which is a kindness: in C it silently compiles and has caused decades of bugs.

Combining questions

age = 25
has_ticket = True
banned = False

print(age >= 18 and has_ticket)
print(age >= 65 or has_ticket)
print(not banned)
print(age >= 18 and has_ticket and not banned)
True
True
True
True
OperatorTrue whenThink of it as
andboth sides are truethe strict bouncer
orat least one side is truethe generous bouncer
notflips true to falsethe contrarian

Python uses the English words, not && and || like most other languages. Read your conditions out loud: if the sentence makes sense in English, it is probably right.

Chained comparisons, a genuine Python nicety

score = 75

print(0 <= score <= 100)          # Python allows this and it means what you think
print(70 < score < 80)
True
True

Most languages force you to write score >= 0 and score <= 100. Python lets you write it the way a mathematician would. It is one of those small touches that make people fond of the language.

Truthiness: everything can answer the question

Python will happily treat any value as a yes or no. The rule is simple and worth committing to memory: empty and zero are false, everything else is true.

print(bool(0), bool(1), bool(-1))
print(bool(""), bool("a"), bool(" "))
print(bool([]), bool([1]))
print(bool({}), bool({"a": 1}))
print(bool(None))
False True True
False True True
False True
False True
False

This is why real Python code looks like this:

name = ""

if not name:
    print("You did not give me a name.")

crew = ["Otis"]
if crew:
    print(f"There are {len(crew)} aboard.")
You did not give me a name.
There are 1 aboard.

if crew: rather than if len(crew) > 0:. Shorter, and every Python programmer reads it instantly. Note the space in bool(" ") is True: a space is a character, so the string is not empty. That distinction has ruined many a form validator.

None: the value that means "nothing here"

winner = None

print(winner)
print(winner is None)
print(type(winner))
None
True
<class 'NoneType'>

None is Python's way of saying "deliberately empty". It is not zero and not an empty string; it is the absence of a value. Functions that do not return anything return None, and it is the standard placeholder for "not decided yet".

== versus is

a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)     # same contents?
print(a is b)     # the very same object in memory?
print(a is c)
True
False
True
PERCEPTION[Medium: Success]

Two identical twins are equal but not identical. Point at one twin and give the finger a second name, and now you have two names for one person: that is 'is'. Python's == asks 'do these look the same', is asks 'are these literally the same thing'.

The rule in practice: use == for everything, except when comparing against None, True or False, where the convention is is. So: if value is None:, always.

Short circuits

def expensive_check():
    print("  (this ran)")
    return True

print("First:")
result = False and expensive_check()

print("Second:")
result = True or expensive_check()

print("Neither of those printed the marker, because Python stopped early.")
First:
Second:
Neither of those printed the marker, because Python stopped early.

With and, if the left side is false the answer is already false, so Python never looks at the right side. This is not just an optimisation, it is a tool: it lets you write if name and name[0] == "G": without crashing on an empty name, because the second half never runs when the first half fails.

Exercise 1

Can they ride?

A rollercoaster requires a height of at least 140cm, an age of at least 12, and that the rider is not currently holding a full mug of grog. Write the condition and test it with a few values.

Reveal solution
height = 152
age = 14
holding_grog = False

can_ride = height >= 140 and age >= 12 and not holding_grog
print(f"Cleared to ride: {can_ride}")

# and one who is not
print(f"Second rider:    {130 >= 140 and 30 >= 12 and not False}")
Cleared to ride: True
Second rider:    False
Exercise 2

Predict the truthiness

For each, say True or False before running.

print(bool("False"))
print(bool(0.0))
print(bool([0]))
print(bool(" "))
print(None == False)
Reveal solution
  1. True. It is a non-empty string. The contents are irrelevant.
  2. False. Zero is zero, even as a float.
  3. True. The list has one item in it. That the item is falsy does not matter.
  4. True. A space is a character.
  5. False. None is not False, it is None. This is exactly why the convention is is None.
Exercise 3

Fix the login check

This is supposed to allow entry when the password matches and the account is not locked. It has two bugs.

password = "grog"
locked = False

if password = "grog" and locked == True:
    print("Welcome")
Reveal solution

Bug one: = instead of ==, which is a SyntaxError. Bug two: the logic is backwards; it demands the account is locked.

password = "grog"
locked = False

if password == "grog" and not locked:
    print("Welcome")
Welcome

not locked rather than locked == False: shorter, and it reads like the sentence you would say out loud.

+100 XP