Why this matters
Almost every program makes a decision. It checks whether a user is logged in, whether a stock count is above zero, or whether a game character touched a wall. Each decision is a test that gives a yes or a no, and the program then does one thing or another. In Python, the yes or no is a value of type bool, and the test is written after an if.
The mistake this lesson is about is small. Two equals signs mean "compare these two values". One equals sign means "store this value in that name". Writing one when you meant two is one of the oldest bugs in C-style languages. Python refuses the simple form of this mistake, which is good. It still allows other forms that look right and run without an error, and those are the ones this lesson trains you to hear.
This lesson does not cover the numeric types. Those are covered in the earlier lessons of this module.
What a bool is
A bool is a value that can be only one of two things: True or False. The name comes from George Boole, a mathematician who studied logic with two values in the 1800s. In Python the type is bool, and the two values are written with capital letters, True and False.
A bool is useful because a program often needs an answer to a yes or no question. Here is the smallest useful example:
It is raining: TrueThe line is_raining = True creates a name that points to the value True. A name is a label for a value. The value is a bool, and it can be only True or False. The line prints the words It is raining: True.
A condition is an expression that gives a bool. An expression is a group of values and operators that gives one result. The condition score > 50 is an expression. It gives True when the value of score is more than 50, and False otherwise. The > sign is an operator that compares two numbers and returns a bool.
An if statement runs a block of code only when a condition is true. A block is the set of indented lines under the if line. Here is an if statement with a condition:
You passed.
The program continues.The condition score > 50 is True because 72 is more than 50. So Python runs the indented line that prints You passed.. The last line runs in both cases, because it is not indented under the if. Keep that picture in mind: a condition is tested, and its bool result decides whether the block runs.
The equals signs
Python has three kinds of symbols that look alike, and beginners mix them up:
=is the assignment operator. It stores the value on its right in the name on its left.x = 5means "put 5 under the name x".==is the equality operator. It asks whether two values are the same.x == 5means "is x equal to 5?" and givesTrueorFalse.!=means "not equal".x != 5givesTruewhen x is anything other than 5.
The first two look almost the same on the screen, and they do completely different jobs. The mistake is to write = when you meant ==. Python reads what you wrote, not what you meant, and in an if line it does not accept a plain assignment:
x = 5
if x = 0:
print("inside") File ".../eq.py", line 2
if x = 0:
^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?The message names the fix. The program does not run at all, so the wrong test cannot reach a user. Java blocks the same mistake for numbers, but with a type error.
To see why the symbol matters, read the code out loud. The = sign should be read as "becomes" or "is set to". The == sign should be read as "is equal to". A condition written with == is read aloud as "if x is equal to zero". The same condition written with one = is read aloud as "if x becomes zero". The second sentence sounds wrong, and that is the point. Reading the condition aloud is a cheap way to catch the mistake before you run the program.
Try this rule whenever you write an if statement: say the condition in words, using "is equal to" for ==. If the spoken version is not what you want, fix the symbol.
The walrus operator: a deliberate assignment in a test
Python does have a form that assigns inside an expression. It is the walrus operator, written :=, and it is named after the shape of the symbol on its side. The walrus stores a value and gives the same value to the test. It must be written on purpose, so it is not the same as the accident above.
inside the else
x is 0The line if (x := 0): first stores 0 in x, which overwrites the 5. Then the value 0 is tested. Zero counts as false, so the else branch runs. The program gave a surprising result, but Python did not refuse it, because the walrus is legal syntax. This is why the rule "read it aloud" still matters. When you see := inside a condition, say "if x becomes 0", and check that this is what you meant. Most code does not need the walrus inside an if.
Truth values: what counts as false
Python does not require a bool in an if. Any value is tested for its truth value, and most values are true. The values that count as false are False, None, the number 0 (including 0.0), and the empty values such as "", [] and {}. Everything else counts as true.
False False False False
True False TrueThe second line shows two cases that surprise beginners. The text "0" is not empty, so it is true. The list [0] has one item, so it is true. The float 0.0 is false, as the rule above says.
This is a second kind of trap. A program that writes if count: treats 0 as "no value", which is right for some questions and wrong for others. If the count can be legitimately 0, use an explicit comparison such as if count > 0:. For None, which means "no value was given", the test if value is None: is the usual form.
Equality and bool values
Equality between a bool and a number has a surprise too. In Python, True counts as 1 and False counts as 0 in arithmetic, and == follows the same rule.
True False
True False False
2 5The first line shows that flag == True is True when flag holds 1, while flag is True is False, because 1 and True are separate values. The second line shows that an empty list and an empty string are not equal to False, even though both are false in an if. The third line shows the arithmetic: True + True is 2.
The rule for a clean program is short. Compare a bool with if flag: or if not flag:. Use is only for None. Avoid == True and == False, because they add a second place to make a mistake, and they behave differently for values that are not bools.
Combining conditions
Real conditions often join several tests. Python has three words for this. and means both sides must be true. or means at least one side must be true. not flips a bool. The sides are evaluated from left to right, and if the left side already decides the answer, the right side is not checked.
The following program decides whether a member can borrow another book from a library. The rule has three parts: the member must have a card, must not be banned, and must have fewer than five books out. Each part is a separate bool, and the program combines them.
can borrow: True
after borrowing more, can borrow: False
Return a book first.Read the condition aloud: "has a card, and not banned, and books out is less than the limit". Each part has its own name, so the test is easy to check against the rule. A reader can match each part of the code to one sentence of the library policy. That matching is the real skill. Long conditions with no names are hard to check, and a single = in the middle of one is hard to see.
Python lets you write a range test as one comparison. The expression 18 <= age < 65 means 18 <= age and age < 65, and it reads the same way as the rule.
False
True
TrueThe not in the last two lines applies to the whole comparison. Python reads not age >= 18 as not (age >= 18), because comparisons are evaluated before not. Some writers still add the brackets for clarity, as the third line does.
The words and and or do more than combine bools. They return one of the two values they join, which is why they are used to pick a default value:
guest
0 5
0 False TrueThe first line picks "guest" because the empty name is false. The second line shows 0 and 5 gives 0, because the left side is already false, and 3 and 5 gives 5, the right side. The third line shows the same idea. Use this form for a simple default. For a more complex choice, write an if.
Conditions that call a function
A function is a named block of code that can be run from other code. It can take input values, called parameters, and it can give back a result. A function that returns a bool can be used directly as a condition. This keeps the if short, and the function name says the question out loud.
cannot vote yetThe function is_adult takes one value called age. It returns the result of age >= 18, which is a bool. In the if line, the call is_adult(age) gives a bool, so the test works. The output is cannot vote yet, because 16 is less than 18.
Now try the trap on a function call. A programmer who has been writing = by habit might write this:
if is_adult(age) = True:
print("can vote")Python refuses the line before it runs:
File ".../call.py", line 4
if is_adult(age) = True:
^^^^^^^^^^^^^
SyntaxError: cannot assign to function call here. Maybe you meant '==' instead of '='?The left side of = must be a name or another place where a value can be stored. A function call gives a value but is not a place to store one, so there is nothing to assign to. The message names the fix.
A rule written as code, step by step
Real rules often come as a sentence. The method is to split the sentence into named parts, write each part as a bool, and then combine them. Take the rule "unlock the phone when the PIN matches and the phone is not locked". It has two parts: the PIN matches, and the phone is not locked. The word "and" becomes and.
unlockedEach part of the sentence has one place in the code. The reader can check the code against the sentence without knowing Python well.
Now the same rule with the mistake, a single = where == belongs:
if entered_pin = pin and not locked:
print("unlocked") File ".../pin.py", line 4
if entered_pin = pin and not locked:
^^^^^^^^^^^^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?The message points at the assignment. Read the rule aloud as the code is written, "if entered PIN becomes PIN", and the mistake is clear.
Common mistakes
Writing = in an if. if isOpen = True: is a SyntaxError in Python. The fix is if is_open: or if is_open == True:, and the first form is the usual one. If the code reads "if is_open becomes True" when you say it aloud, the symbol is wrong.
Treating 0 as missing. if count: is false when count is 0, which may be a real count. Use if count is None: to test for a missing value, and if count > 0: to test for a positive count. Choose the test that matches the meaning.
Reading not as applying to one name. not age >= 18 runs without an error and means not (age >= 18). Writing not age instead tests whether age is false, which is a different question. Put the brackets around the comparison when a reader might confuse the two.
Using is with a bool. flag is True is False when flag holds 1, even though flag == True is True. Use if flag: for a bool, and is only for None.
Assuming if flag: and if flag == True: behave differently. For a bool they are the same test. The longer form only adds a second place where a = can be typed by mistake.
Practice
Work each exercise yourself before you open the answer. Predict the output on paper first.
- What does this program print? Explain each line in one sentence.
ready = False
if (ready := True):
print("go")
else:
print("wait")
print("ready is", ready)- Does this program run? If not, what is the error?
n = 3
if n = 3:
print("n is 3")- Read this condition aloud, then say in one sentence what it tests. Then write an equivalent condition that does not use
notin front of the whole expression.
a = False
b = True
if not (a or b):
print("neither")-
A program should print
adultwhenageis 18 or more. Its author wroteif age = 18:. Explain in two sentences why this is wrong, then write the correct condition. -
Rewrite this condition so each part has a name and the whole condition reads like a sentence:
if score >= 50 and attendance >= 75 and not has_failed_project:.
Each exercise has an answer below a "Show the answer" line. Try each one first, then read the answer.
- The walrus stores
Trueinready, and the value of that assignment isTrue, so the first branch runs. The last line printsTrue.
Show the answer
ready = False
if (ready := True):
print("go")
else:
print("wait")
print("ready is", ready)The output is go and then ready is True.
- It does not run. Python reports
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?, because an assignment is not allowed as anifcondition. Changing the line toif n == 3:fixes it.
Show the answer
n = 3
if n == 3:
print("n is 3")The output is n is 3.
- Read aloud: "if not (a or b)". The condition is true only when both
aandbare false. An equivalent form isnot a and not b. For the values above, both forms giveFalse, so the body does not run.
Show the answer
a = False
b = True
print(not (a or b), not a and not b)The output is False False.
- Python refuses the line, because
age = 18is an assignment, not a comparison. The line would also store 18 intoageand test nothing useful. The correct test for "18 or more" isif age >= 18:, which reads aloud as "if age is at least 18" and does not changeage.
Show the answer
age = 20
if age >= 18:
print("adult")- Give each part a name, then combine them.
Show the answer
passed_tests = score >= 50
attended_enough = attendance >= 75
no_failed_project = not has_failed_project
if passed_tests and attended_enough and no_failed_project:
print("pass")The names carry the meaning, and the final if reads aloud as "if passed tests and attended enough and no failed project". The behaviour is the same as the original condition.
What you now know
- A bool holds only
TrueorFalse, and anifruns its block only when its condition is true. =stores a value, and==compares two values. Read==aloud as "is equal to" and=as "becomes".- Python refuses
=inside anif, so the usual form of the mistake is a syntax error that stops the program. The walrus:=assigns on purpose and still needs care. - Any value can be tested.
0,"",[]andNonecount as false, soif count:can give the wrong answer when 0 is a real count. - Use
and,orandnotto combine conditions, and name each part of a long condition in its own variable.