Python9 min readCode verified

Booleans, if/elif, and the Condition That Is Always True

One of these conditions reads like plain English, compiles without complaint, runs without error, and is true no matter what the variable holds. Every result on this page was executed, not remembered.

Booleans and comparison#

A boolean has exactly two values, True and False — capitalised, and not in quotes. A comparison produces one:

print(5 == 5)
print(5 == 6)
print(type(True))
True
False
<class 'bool'>

The six comparison operators:

OperatorTrue when
==the two values are equal
!=they are not equal
>   <strictly greater / strictly less
>=   <=greater or equal / less or equal

The one-character bug#

= assigns. == compares. Putting the wrong one in an if is caught immediately, and the error message tells you the fix:

x = 5
if x = 5:
    print("hi")
  File "cond.py", line 2
    if x = 5:
       ^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

Python is doing you a favour here. In several other languages that line is legal, silently assigns, and quietly becomes a condition that is always true.

and, or, not#

x = 6
print(x > 0 and x < 10)
print(x > 0 or x < -10)
print(not x > 0)
True
True
False

and needs both sides true. or needs at least one. not flips whatever follows it.

Python also allows a range check to be written the way you would write it in mathematics, which most languages do not:

x = 6
print(0 < x < 10)
True

The condition that is always true#

Here is the one worth the whole page. You want to know whether number is 5, 6 or 7, and you write what you would say out loud:

number = 5
print(number == 5 or 6 or 7)
True

Correct. Now try a number that is none of the three:

number = 99
print(number == 5 or 6 or 7)
6

Not False. Not even a boolean. It printed 6.

Python read the line as (number == 5) or 6 or 7. The comparison only ever applied to the 5. The 6 and the 7 are just values sitting there, and or returns the first operand that counts as true — number == 5 is False, so it moves on to 6, which is non-zero, so it stops and returns it.

Inside an if, 6 counts as true. So the condition fires for every possible value of number, and the program has no error, no warning, and no crash — just a branch that always runs.

Both correct forms:

number = 99
print(number == 5 or number == 6 or number == 7)
print(number in (5, 6, 7))
False
False
The rule underneath

Each side of an and or an or must be a complete comparison on its own. If you cannot delete everything on the other side of the operator and still have a sentence that makes sense, the expression is not doing what you think.

Precedence#

not binds tighter than and, which binds tighter than or. All three bind looser than the comparison operators, which is why x > 0 and x < 10 needs no brackets.

print(not True and False)
print(not (True and False))
False
True

The first line is (not True) and False. Use brackets whenever the reading is not instant — they cost nothing and they are never wrong.

if, elif, else — and the bug that prints everything#

grade = 85
if grade >= 90:
    print("A")
elif grade >= 80:
    print("B")
elif grade >= 70:
    print("C")
else:
    print("F")
B

An if/elif chain runs at most one branch. The moment a condition matches, everything below is skipped — which is exactly why the chain can be written in descending order without upper bounds.

Replace the elifs with separate ifs and it still runs, still reports no error, and is wrong:

grade = 95
if grade >= 90:
    print("A")
if grade >= 80:
    print("B")
if grade >= 70:
    print("C")
A
B
C

Three independent questions, three independent answers. A 95 really is greater than 90, and 80, and 70.

What counts as true#

An if accepts any value, not just a boolean. The rule is empty or zero is false, everything else is true:

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

Look hard at bool("0"). It is True — the string is one character long, so it is not empty. Since input() always hands you a string, if answer: is true even when the user typed a zero.

That same "input is always a string" rule catches people one step earlier:

answer = "5"
print(answer == 5)
False

A string is never equal to a number, no matter how similar they look. Convert with int() first, or compare against "5".

Boolean functions#

A function whose return is a comparison hands back a boolean, and reads beautifully inside an if:

def is_divisible(x, y):
    return x % y == 0

print(is_divisible(100, 10))
print(is_divisible(7, 2))
True
False

Note that return x % y == 0 needs no if at all — the comparison already is the answer. Writing if x % y == 0: return True else: return False is four lines that do the work of one.

The same applies at the call site:

def is_even(n):
    return n % 2 == 0

if is_even(4) == True:
    print("even")
if is_even(4):
    print("even again")
even
even again

Both work. The second is what you want — comparing a boolean to True is asking "is this true, true?"

pass, for the branch you have not written yet#

age = 60
if age > 55:
    pass
print("done")
done

Python requires an indented body after an if. pass is a body that does nothing, so you can sketch the branch structure now and fill it in later. Leaving the body out entirely is an IndentationError.

Never compare floats with ==#

print(0.1 + 0.2 == 0.3)
print(abs((0.1 + 0.2) - 0.3) < 1e-9)
False
True

Floats are stored in binary, and one tenth has no exact binary form for the same reason one third has no exact decimal form. The sum lands a hair away from 0.3. Compare the size of the difference against a small tolerance instead.

Where these go wrong#

  • x == 5 or 6 or 7. Always true, never warns. Each side of an or needs its own complete comparison.
  • Separate ifs where you meant elif. Every matching branch runs, so more than one prints.
  • Comparing input() to a number. "5" == 5 is False. Convert first.
  • Assuming "0" is false. It is a non-empty string, so it is true.
  • Comparing floats with ==. Use a tolerance.
  • Forgetting the colon after if, elif or else, or not indenting the body.
  • An elif chain in the wrong order. Put grade >= 70 first and every passing grade becomes a C, because the first match wins.
  • if is_even(n) == True:. Harmless, but it says the quiet part twice.

More Python guides

Free study guides for a first Python course — every listing executed before publication.

See the Python course →

Frequently asked questions#

Why is number == 5 or 6 or 7 always true?

Because Python reads it as (number == 5) or 6 or 7. The 6 is not compared to anything — it is a value on its own, and any non-zero number counts as true, so the whole expression is true whatever number holds. Write number == 5 or number == 6 or number == 7, or number in (5, 6, 7).

What is the difference between = and == in Python?

A single = assigns a value; a double == compares two values. Using = in an if condition is a syntax error in Python, and the message even suggests the fix: SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

What is the difference between elif and a second if?

An if/elif chain runs at most one branch — once a condition matches, the rest are skipped. Separate if statements are each tested independently, so a grade of 95 prints A, B and C instead of just A.

What counts as True in Python?

Anything that is not empty and not zero. 0, 0.0, the empty string, the empty list and None are false; every other number, every non-empty string and every non-empty collection is true. Note that the string "0" is not empty, so it is true.

Why does my comparison with input fail?

Because input always returns a string, and a string is never equal to a number: "5" == 5 is False. Convert first with int() or float(), or compare against a string.

Why is 0.1 + 0.2 == 0.3 False?

Because floats are stored in binary and one tenth has no exact binary form, so the sum is very slightly off. Never compare floats with ==; check that the absolute difference is smaller than a small tolerance instead.