Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Python Module 2 – Values, variables, numbers and strings

Booleans, None, truthiness and comparisons

Predict which Python values are truthy or falsy, use and, or and not knowing they return operands, and compare correctly with ==, is and chained comparisons.

  • Beginner
  • 18 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot

What you will learn

  • Predict the truthiness of common values
  • Use and, or and not, knowing they return operands
  • Compare with ==, is and chained comparisons correctly

Before you start

On this page

Programs keep asking yes-or-no questions: is the list empty, did the user type anything, is the score in range? Python’s answers are the two values True and False, but if, while, and, or and not accept almost any value, because nearly every object counts as either true or false. This lesson shows which values count as which, what and and or really return, how None fits in, and how to compare values without the classic traps.

True, False and the bool type

True and False are the only two objects of type bool. Comparisons such as 3 > 2 produce them, and bool(value) converts any value to one of them. Under the hood, bool is a kind of int, with True worth 1 and False worth 0:

bool is a kind of int Python · bool_is_int.py
print(True == 1, False == 0)
print(True + True)
print(sum([True, False, True]))  # adding bools counts the True values
print(isinstance(True, int))  # bool is a kind of int

print(bool("False"))  # careful: any text that is not empty is true

Output

True True
2
2
True
True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bool_is_int.py

That is why sum() of a list of bools counts the True values. Python’s documentation still discourages relying on bools behaving as numbers: when you mean a number, convert with int(flag). The last line is a common mistake when reading answers from users or files: bool("False") is True, because any string that is not empty is true. Compare the text instead, as in answer == "yes".

Version note

Using ~ (bitwise “not”) on a bool has been deprecated since Python 3.12 and is due to become an error in Python 3.16: ~True is the int -2, not False. Write not flag.

Truthiness: which values count as true

In an if or while condition, and as an operand of and, or and not, Python treats a value as falsy or truthy. The falsy values are few and worth knowing by heart:

  • None and False;
  • zero of any number type: 0, 0.0, 0j (and Decimal(0) and Fraction(0) from the standard library);
  • empty strings and empty collections: "", (), [], {}, set(), range(0).

Everything else is truthy, unless its class defines otherwise (classes are a later module).

Twelve values, true or false Python · truth_table.py
values = [None, False, 0, 0.0, "", [], {}, "0", " ", "False", [0], float("nan")]
for value in values:
    print(repr(value).ljust(8), bool(value))

Output

None     False
False    False
0        False
0.0      False
''       False
[]       False
{}       False
'0'      True
' '      True
'False'  True
[0]      True
nan      True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 truth_table.py

The last five values hold the surprises: the strings "0", " " and "False" are not empty, the list [0] has one item, and NaN is a float that is not zero, so all five are truthy. In practice, truthiness gives short, readable tests: PEP 8 recommends if items: and if not items: rather than if len(items):.

Version note

New in Python 3.14: NotImplemented, a special value that the comparison and arithmetic methods of classes return when they cannot handle an operand, raises TypeError when it is used as a condition. Python 3.9 to 3.13 only warned about it.

and, or and not

and and or evaluate their operands from left to right and stop as soon as the answer is known. They do not turn the answer into a bool: the result is the operand that decided it.

Two flowcharts: x or y returns x when x is true, otherwise y; x and y returns x when x is false, otherwise y.x or yx and yIs x true?Result: x(y is not evaluated)Evaluate yResult: yIs x true?Result: x(y is not evaluated)Evaluate yResult: yyesnoyesno

How or and and choose their result

Text description of the diagram

Two small flowcharts, one above the other. Both start by testing whether x is true (truthy).

  • x or y: if x is true, the result is x and y is not evaluated at all. If x is false, Python evaluates y and the result is y.
  • x and y: if x is false, the result is x and y is not evaluated at all. If x is true, Python evaluates y and the result is y.

In both cases the result is one of the two operands, not necessarily True or False.

What and and or return Python · and_or_values.py
print("" or "guest")  # or gives the first operand that is true ...
print("Asha" or "guest")
print(0 or None)  # ... or the last one, when none is true

print([] and 10)  # and gives the first operand that is false ...
print(5 and 10)  # ... or the last one, when none is false
print(0 and 1 / 0)  # 0 is false, so 1 / 0 is never evaluated

print(not "text", not 0)  # not always gives True or False

Output

guest
Asha
None
[]
10
0
False True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 and_or_values.py

  • x or y gives x if x is true, and otherwise y. So "" or "guest" is "guest", a handy default for empty text.
  • x and y gives x if x is false, and otherwise y. In 0 and 1 / 0, the 0 already decides the answer, so the division never runs and there is no ZeroDivisionError: this is called short-circuiting.
  • not is different: it always returns True or False.

A common mistake: or throws away a valid zero

The or default treats every falsy value as missing, and that includes 0 and the empty string, which are often perfectly good answers:

When 0 is a real answer Python · zero_trap.py
saved_volume = 0  # the user turned the sound off on purpose

volume = saved_volume or 50  # bug: the 0 is falsy, so it is replaced
print(volume)

volume = 50 if saved_volume is None else saved_volume  # only None means "not set"
print(volume)

Output

50
0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 zero_trap.py

The user chose silence, and saved_volume or 50 turned it back up to 50. The fix tests for the one value that really means “not set”. a if condition else b is a conditional expression: it evaluates the condition and gives a when it is true and b otherwise (the next lesson places it among the operators).

None, the “no value” value

None is the only object of its type, NoneType, and it stands for “no value here”: a setting that was not given, a result that does not exist, a search that found nothing. Functions that return nothing in particular return None:

Meet None Python · none_basics.py
result = print("Hello")  # print() shows the text and returns None
print(result)
print(result is None)
print(type(None))
print(None == 0, None == False, not None)  # falsy, but equal to neither

Output

Hello
None
True
<class 'NoneType'>
False False True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 none_basics.py

  • Test for it with is None and is not None, as PEP 8 recommends: there is exactly one None, and is checks for that very object, which no class can redefine.
  • None is falsy, but it is not equal to 0 or to False. Write value is None, not not value, when 0 or an empty string are valid values.
  • None takes part in no arithmetic and no ordering, so a missing value stops a program the moment it is used:
A missing score in a list Python · missing_score.py
scores = [72, None, 85]  # one student was absent
print(max(scores))

Output (exit status 1)

Printed as an error (standard error)

Traceback (most recent call last):
  File "missing_score.py", line 2, in <module>
    print(max(scores))
          ~~~^^^^^^^^
TypeError: '>' not supported between instances of 'NoneType' and 'int'

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 missing_score.py

max() compares the items with >, and None cannot be compared with a number. Leave missing values out, or replace them, before you compute with the list.

Comparisons

Python has four kinds of comparison:

  • == and != compare values: 1 == 1.0 is True.
  • <, <=, > and >= compare order: "apple" < "banana" is True.
  • is and is not compare identity, whether both sides are the very same object: None is None is True.
  • in and not in test membership, which the next lesson covers: "a" in "cat" is True.

With the built-in types, all of them give a bool. They share one precedence level, and you can chain them: 0 <= marks <= 100 means 0 <= marks and marks <= 100, with marks evaluated once, and Python stops at the first part that is false.

Chained comparisons Python · chained.py
marks = 75
print(0 <= marks <= 100)  # means 0 <= marks and marks <= 100
print(1 < 2 < 3, 3 > 2 > 1)

print(1 < 2 == True)  # chained too: 1 < 2 and 2 == True
print((1 < 2) == True)  # brackets end the chain

print(1 == 1.0, "1" == 1)  # numbers compare by value; text never equals a number
print("apple" < "banana", "Zebra" < "apple")  # text compares by code point

Output

True
True True
False
True
True False
True True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 chained.py

  • Chaining applies to every comparison operator, which is what makes 1 < 2 == True a puzzle: it means 1 < 2 and 2 == True, and 2 is not equal to True (which is 1), so the whole expression is False. Brackets end the chain. Better still, never compare with True or False at all: write if done:.
  • Numbers of different types compare by value, so 1 == 1.0 is True; a string is never equal to a number, and ordering a string against a number raises TypeError.
  • Strings are ordered character by character by their code points, in which the capitals A to Z all come before the small letters a to z: "Zebra" < "apple".

== or is?

== asks whether two objects have the same value; is asks whether they are the same object. Two lists built separately are equal but not identical:

Equal or identical Python · is_vs_eq.py
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)  # the same value
print(a is b)  # but two different lists

c = a
print(c is a)  # two names for one list

answer = None
print(answer is None)  # the right way to test for None

Output

True
False
True
True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 is_vs_eq.py

Use is for None (and for the rare object you create only to compare identity with), and == for everything else. For numbers and strings, Python may or may not reuse one object for equal values, depending on the version and on how the code was compiled, so is with them gives unreliable answers. Python 3.8 and newer warn when they see it used with a literal:

is with a number Python · is_literal.py
count = 300
print(count is 300)  # Python warns: is compares identity, not value
print(count == 300)

Output

True
True

Printed as an error (standard error)

is_literal.py:2: SyntaxWarning: "is" with 'int' literal. Did you mean "=="?
  print(count is 300)  # Python warns: is compares identity, not value

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 is_literal.py

The program runs and the warning appears on standard error. The True from count is 300 comes from how CPython compiled this file, not from any rule of the language; a different program could print False.

Python Online Compiler Try your own truth tables and chained comparisons in the browser.

Key takeaways

  • True and False are a kind of int (1 and 0), but convert with int() when you mean a number.
  • Falsy: None, False, zeros, and empty strings and collections. Everything else, including "0", "False" and [0], is truthy.
  • and and or short-circuit and return one of their operands; not always returns a bool.
  • x or default also replaces 0 and ""; test x is None when only None means “missing”.
  • Chained comparisons such as 0 <= marks <= 100 read like maths, and every comparison operator chains.
  • Use == for values and is only for None and other single objects; never compare with True or False.

Exercise

Exercise · Easy · Python

Turn marks into a letter grade

Write grade(marks), which turns marks out of 100 into a letter grade on this scale (the cut-offs are for this exercise; every college publishes its own):

  • from 90 to 100: "O"
  • from 80 up to, but not including, 90: "A+"
  • from 70 up to 80: "A"
  • from 60 up to 70: "B+"
  • from 50 up to 60: "B"
  • from 40 up to 50: "C"
  • from 0 up to 40: "F"

Marks may be whole numbers or have decimals: grade(89.5) is "A+", and grade(90) is "O". For marks below 0 or above 100, raise ValueError. Use chained comparisons such as 80 <= marks < 90; the sample tests check every cut-off from both sides.

Starter code · grades.py

def grade(marks):
    """Return the letter grade for marks from 0 to 100; raise ValueError outside that range."""
    # Replace this line with your code.
    return "F"
The sample tests · test_grades.py
from grades import grade


def raises_value_error(marks):
    """True when grade(marks) raises ValueError."""
    try:
        grade(marks)
    except ValueError:
        return True
    return False


def test_top_grade():
    """gives O from 90 up to 100"""
    assert grade(100) == "O"
    assert grade(95.5) == "O"
    assert grade(90) == "O"


def test_middle_grades():
    """gives A+, A, B+ and B from their cut-offs up to just below the next one"""
    assert grade(89.99) == "A+"
    assert grade(80) == "A+"
    assert grade(79.5) == "A"
    assert grade(70) == "A"
    assert grade(69) == "B+"
    assert grade(60) == "B+"
    assert grade(59.9) == "B"
    assert grade(50) == "B"


def test_lower_grades():
    """gives C from 40 and F below 40, down to 0"""
    assert grade(49.5) == "C"
    assert grade(40) == "C"
    assert grade(39.99) == "F"
    assert grade(0) == "F"


def test_out_of_range():
    """raises ValueError for marks below 0 or above 100"""
    assert raises_value_error(-1)
    assert raises_value_error(-0.5)
    assert raises_value_error(100.5)
    assert raises_value_error(101)
A hint

Check the range first: if not 0 <= marks <= 100: catches both kinds of bad marks in one test, and raise ValueError("…") stops the function. Then test the grades from the top down with if and elif; each test is a chained comparison such as 80 <= marks < 90, which is true for 80 and for 89.99 but not for 90.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 5 Which of these values are falsy?

    Choose every answer that is right.

    Show the answer to question 1

    Answer:

    • None
    • 0
    • []
    • ""

    Zeros, empty strings and empty collections are falsy, and so are None and False. "0" and "False" are strings with characters in them, and [0] is a list with one item, so all three are truthy.

  2. Question 2 of 5 What does and_or_values.py print?

    What does this program print? Choose one answer.

    print("" or "guest")  # or gives the first operand that is true ...
    print("Asha" or "guest")
    print(0 or None)  # ... or the last one, when none is true
    
    print([] and 10)  # and gives the first operand that is false ...
    print(5 and 10)  # ... or the last one, when none is false
    print(0 and 1 / 0)  # 0 is false, so 1 / 0 is never evaluated
    
    print(not "text", not 0)  # not always gives True or False
    Show the answer to question 2

    Answer: it prints

    guest
    Asha
    None
    []
    10
    0
    False True

    or gives the first true operand, or the last operand when none is true (0 or None is None). and gives the first false operand, or the last one when none is false (5 and 10 is 10). Only not turns its result into a bool.

  3. Question 3 of 5 What does this print?

    Read the code, then choose one answer.

    print(1 < 2 == True)
    Show the answer to question 3

    Answer: False

    Every comparison operator chains, so this means 1 < 2 and 2 == True. The first part is true, but 2 is not equal to True, which is worth 1, so the result is False. (1 < 2) == True would be True.

  4. Question 4 of 5 limit may be a number, including 0, or None when no limit was set. Which test finds out that no limit was set?

    Choose one answer.

    Show the answer to question 4

    Answer: if limit is None:

    Only limit is None is true for None alone. not limit is also true for a limit of 0, limit == False is true for 0 and false for None, and limit is False is never true here.

  5. Question 5 of 5 What does this print?

    Read the code, then choose one answer.

    a = [1, 2]
    b = [1, 2]
    print(a == b, a is b)
    Show the answer to question 5

    Answer: True False

    The two lists hold equal values, so == is True, but each [1, 2] creates a separate list, so they are not the same object and is is False.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.