Operators and Expressions
Learn how Python builds expressions from arithmetic, comparison, boolean and assignment operators, and why floor division, precedence and truthiness surprise beginners
A support ticket arrives with a screenshot, and the number on it is wrong. A customer bought three workshop seats at 10 euros each, with a 2 euro launch discount on every seat, and the receipt says 4 euros.
price = 10
seats = 3
total = price - 2 * seats
print(total)The developer read the line as "ten minus two, then times three". Python read
it as "ten minus the result of two times three". Both readings feel obvious to
the person who wrote them, and only one of them is what the interpreter does.
The correct line is total = (price - 2) * seats, which charges 24 euros.
The bug is not a typo, and no library can rescue it. It is a missing model of how a line of code becomes a value: which symbols act on which neighbours, in what order, and what kind of result each step hands back. That model is the single most useful thing you can carry into every Python file you will read.
You will meet four families of operators, learn what each one returns, and see why division, comparison and boolean logic each hide one famous trap. Every example is checkable in a Python 3.15 REPL, and the practice questions reuse the same numbers so you can follow each step by hand.
Key terms
- A symbol that asks Python to do something with the values beside it, such as + for addition or == for equality.
- A value that an operator acts on. In 2 * seats, the operands are the int 2 and whatever object the name seats points at.
- A piece of code that Python evaluates to produce one value, such as 2 + 3 or price - 2 * seats.
- Work out the value of an expression by following the operator rules, from the innermost or highest precedence part outward.
- The ranking that decides which operator runs first when several appear in one expression, so multiplication beats addition.
- The rule that decides whether an object counts as true or false in a condition. Empty collections and zero are falsy.
- The habit of and and or to stop evaluating as soon as the answer is settled, without touching the remaining operand.
- Whether two names point at the very same object in memory. The is operator tests identity, while == tests equal value.
- A combined operator such as += or //= that reads a name, applies an operation and binds the name to the result.
Operator
Operand
Expression
Evaluate
Precedence
Truthiness
Short circuit
Identity
Augmented assignment
1. Expressions, Operators and Operands
Start with the receipt line and take it apart. The right side of the = sign
is an expression, and Python reduces it to a single value before the name on
the left receives that value. Inside the expression, the symbols - and *
are operators, and the values they work on are operands.
price = 10
seats = 3
total = price - 2 * seats # the expression on the right is evaluated firstAn expression can be as small as one literal or as large as a page of nested calls. What matters is that every expression has a value and a type, and both are decided by the operators inside it. Arithmetic returns a number, comparison returns a bool, boolean logic returns one of its operands, and an assignment line changes a name rather than producing a reusable value. Keep those four result kinds in mind, because most beginner bugs come from expecting the wrong one.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A line of code reads `total = price - 2 * seats`. Which description correctly names the operators, the operands and the result?
2. Arithmetic: Seven Signs and Their Result Types
Arithmetic is the family you met first, but its result types are where the surprises live. Python has seven everyday arithmetic operators, and each one has a rule about the kind of object it returns.
Operator | Name | Example | Result |
|---|---|---|---|
+ | addition | 10 + 3 | 13 |
- | subtraction | 10 - 3 | 7 |
* | multiplication | 10 * 3 | 30 |
/ | true division | 10 / 3 | 3.3333333333333335 |
// | floor division | 10 // 3 | 3 |
% | remainder | 10 % 3 | 1 |
** | exponentiation | 10 ** 3 | 1000 |
Division always hands back a float
The forward slash performs true division, and true division always returns a float, even when the numbers divide evenly.
>>> 6 / 3, 4 / 2
(2.0, 2.0)
>>> type(6 / 3)
<class 'float'>Floor division rounds down, not toward zero
The double slash performs floor division, and floor means the next lower whole number, not the number closer to zero. That difference appears the moment a negative value arrives.
>>> 7 // 2, -7 // 2, -7.0 // 2
(3, -4, -4.0)For positive numbers the two ideas agree, which is why the trap waits until a
refund, a temperature or a score below zero. There, -7 // 2 gives -4,
because negative three point five floors down to negative four.
The remainder follows the divisor
The percent sign returns the remainder, and the sign of that remainder follows the divisor rather than the dividend.
>>> -7 % 3, 7 % -3, -7.5 % 2
(2, -2, 0.5)The pair // and % always satisfies a == (a // b) * b + (a % b), even for
negative numbers, and divmod hands you both parts in one call.
>>> divmod(47, 5), divmod(-7, 2)
((9, 2), (-4, 1))
>>> divmod(7.5, 2)
(3.0, 1.5)A bakery with 47 croissants and boxes of 5 fills 9 boxes and has 2 left over,
which is exactly the (9, 2) pair. The choice between /, // and % is
really a choice about the question you are asking, as the diagram shows.
Description
A flowchart for the section The remainder follows the divisor, showing the path and decision points described in the surrounding text.
How to answer
Fill-in-the-blank exercise with 2 blanks. Type the missing words, then check your answer.
Complete the bill split so that the pair holds the full shares and the leftover euros.
bill = 47 people = 5 shares, leftover = blank(bill, people) each = bill blank people
Word Bank, drag words into the blanks above:
Powers group from the right
Exponentiation is right associative, which means a stack of powers is read from the top right downward.
>>> 2 ** 3 ** 2, (2 ** 3) ** 2
(512, 64)
>>> -2 ** 2, (-2) ** 2
(-4, 4)The first power is 2 ** 9, because Python groups 3 ** 2 first and raises 2
to that result. Unary minus binds more loosely than a power on its left, so read
-2 ** 2 as the negative of two squared, not as negative two squared.
Mixed types promote, and bool is a number
When an int and a float meet, the result is a float. Since bool is a subclass of
int, True also behaves as the number 1 in arithmetic.
>>> 1 + 2.5
3.5
>>> True + True
2The plus and star signs also join or repeat sequences, which is your first taste
of operator overloading. "ab" + "cd" is 'abcd', "ab" * 2 is 'abab', and
[0] * 3 is [0, 0, 0]. Neither sign converts between kinds, so "ab" + 2
raises a TypeError rather than guessing what you meant.
📚 Did you know? bool is a subclass of int, so True == 1 is True and
True + True is 2. The bool type was added on top of the existing integer
world, and that arithmetic compatibility was kept on purpose.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A refund rule computes `-7 // 2` for a customer who is owed a negative adjustment. What does Python return, and why?
How to answer
Multiple-choice question with 2 options. Select one option, then choose Check Answer.
The expression `6 / 3` returns the int `2` in Python, because the division has no remainder.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
What does the expression `2 ** 3 ** 2` evaluate to in Python?
3. Comparisons, Identity and Membership
Comparison operators ask a question and answer with a bool.
>>> 5 == 5, 5 != 5, 3 < 4 <= 4
(True, False, True)The six signs are ==, !=, <, <=, > and >=, and they all return
True or False. The double equals sign compares values, while a single equals
sign assigns, and Python refuses to guess which one you meant in a condition.
Two comparisons can share one value
A chain such as 18 <= age <= 65 is not two separate expressions glued
together. It means 18 <= age and age <= 65, and the middle value is evaluated
exactly once.
>>> age = 30
>>> 18 <= age <= 65
True
>>> 0 <= 100 <= 100
TrueIdentity asks a different question
The is operator tests whether two names point at the very same object, while
== tests whether two objects have equal value.
>>> [] == [], [] is []
(True, False)
>>> None is None
TrueTwo empty lists are equal in value but are separate objects, so identity says
no. Small integer objects may be cached inside one interpreter, but that is an
implementation detail and never a rule you should build on. Use == for values
and reserve is for None and other sentinels.
Never compare numbers or strings with is, even when a quick test appears to
work. The result can change between Python versions and between one run and
the next, because object reuse is an internal choice rather than a language
promise.
Membership asks whether a value is inside
The in and not in operators test membership in a string, list, tuple, set
or dictionary.
>>> "a" in "cat"
True
>>> 3 in [1, 2, 3]
True
>>> "tea" not in ["coffee", "juice"]
TrueFor text, membership checks a substring rather than a whole word, so "at" in "cat" is also true. For a dictionary, membership looks at keys by default.
How to answer
Matching exercise with 5 pairs. Match each item on the left with its answer on the right.
Match each operator to the question it answers.
Drag answers from here:
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A gym check uses `18 <= age <= 65`. When `age` is 30, how does Python evaluate that expression?
How to answer
Multiple-choice question with 2 options. Select one option, then choose Check Answer.
The check `[] is []` returns `True`, because the two empty lists hold the same contents.
4. Boolean Operators and Truthiness
The words and, or and not combine conditions. Before they can do that,
Python must decide which values count as true, and that decision is called
truthiness.
>>> bool(0), bool(""), bool([]), bool(None)
(False, False, False, False)
>>> bool(1), bool("x"), bool([0])
(True, True, True)None, False, numeric zero and empty collections are falsy. Almost everything
else is truthy, including the text "False" and the list [0], because a
non-empty container is true regardless of what it contains.
and and or return an operand
This is the point where the intuition from other languages fails. The operators
and and or short circuit, and they hand back one of their operands rather
than a converted bool.
>>> 3 and 4
4
>>> 3 or 4
3
>>> 0 or "x"
'x'
>>> [] or 5
5and returns the first falsy operand, or the last operand if every value is
truthy. or returns the first truthy operand, or the last operand if every
value is falsy. Only not always returns a real bool.
>>> not 3
False
>>> not []
True📚 Did you know? The operand return rule is what makes name = user or "guest" work as a default. When user is empty, or skips it and hands back
the text, so a falsy value never reaches the name.
Short circuit stops the work
Short circuit means the second operand may never be evaluated at all. This is both a performance detail and a safety feature.
>>> 0 and (1 / 0)
0
>>> 3 or (1 / 0)
3Neither line raises a ZeroDivisionError, because the answer was already
settled. Swap the order and the division runs, which is why guards such as
if items and items[0] are safe while the reverse order can fail.
Description
A flowchart for the section Short circuit stops the work, showing the path and decision points described in the surrounding text.
not binds more loosely than a comparison
The word not sits below comparison in the precedence table, so not a == b
means not (a == b).
>>> a, b = 1, 2
>>> not a == b
True
>>> (not a) == b
FalseThe first line asks whether the equality failed, which is true. The second line
asks whether False equals 2, which is false. Add parentheses when the
sentence in your head needs them.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A REPL evaluates `3 and 4` and then `0 or "x"`. Which pair of results is printed?
How to answer
Multiple-choice question with 2 options. Select one option, then choose Check Answer.
The empty list `[]` is falsy in Python, so the expression `[] or 5` returns `5`.
5. Assignment, Augmented Assignment and the Walrus
A single equals sign binds a name to a value. A condition needs a comparison,
so writing if x = 5: is a syntax error rather than a quiet assignment.
x = 10 # assignment
x == 10 # comparison, returns TrueAugmented assignment combines a calculation with a rebinding, and the shorthand covers every arithmetic and bitwise sign.
x = 10
x += 5 # 15
x -= 3 # 12
x *= 2 # 24
x /= 4 # 6.0
x //= 2 # 3.0
x %= 2 # 1.0
x **= 3 # 1.0The /= line is the one to watch, because true division turns an int name into
a float. Every later line then works on a float, which is why the state diagram
marks the type change as a real event.
Description
A state diagram for the section 5. Assignment, Augmented Assignment and the Walrus, showing the states and transitions described in the surrounding text.
Augmented assignment usually rebinds a name to a new object, but mutable
objects are the exception. For a list, += extends the existing list in place
instead of building a fresh one, and that difference matters when two names
point at the same list.
The conditional expression and the walrus
A conditional expression chooses between two values: `label = "pass" if score
= 60 else "retry"`. Read it as this value if the condition holds, otherwise that value, and note that it is an expression rather than a statement.
The walrus operator := assigns a name inside a larger expression, which avoids
evaluating the same call twice.
while (line := file.readline()):
print(line.rstrip())The parentheses are required in most mixed contexts, and the operator is best kept for a read that feeds a test. If a line needs a comment to explain the binding, split it into two ordinary statements instead.
Do not use the walrus to make a line shorter at the cost of clarity. A name created inside a condition lives on after the condition, so a reader has to track it through the rest of the block.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A counter starts as `count = 10`, which is an int. After the line `count /= 2` runs, what are the value and the type?
6. Precedence, Parentheses and the Bug From the Till
Return to the receipt. Python groups 2 * seats before the subtraction because
multiplication outranks subtraction, and the fix is a pair of parentheses.
Precedence is the full ranking that decides this order, and it is longer than
the school acronym suggests. PEP 8 also asks for single spaces around binary
operators, because crowded signs slow a reader down.
Level | Operators | Meaning |
|---|---|---|
1 | ( ) | parentheses, evaluated first |
2 | ** | power, grouping right to left |
3 | +x, -x, ~x | unary signs |
4 | *, @, /, //, % | multiply and divide |
5 | +, - | add and subtract |
6 | << >> & ^ | | bit shifts and bitwise work |
7 | comparisons, in, is | tests, which chain |
8 | not | boolean negation |
9 | and | boolean AND |
10 | or | boolean OR |
11 | if ... else | conditional expression |
The school acronym covers levels 2 to 5 and then stops, which is why it cannot
explain not a == b or a chained comparison. Learn the shape of the table, and
reach for parentheses whenever a reader would need the table to follow you.
>>> 2 + 3 * 4
14
>>> (2 + 3) * 4
20
>>> 2 ** 3 * 4
32
>>> -2 ** 2
-4The sequence diagram follows the first two lines through the same four numbers. The tree changes, and the answer changes with it.
Description
A sequence diagram for the section 6. Precedence, Parentheses and the Bug From the Till, showing the order of messages and replies described in the surrounding text.
Order the steps below to rebuild the buggy line, then compare the order with the fix. The first card names the target, and the last card shows what the receipt prints.
How to answer
Ordering exercise with 4 items. Move them into the correct order, then check your answer.
Order the evaluation steps for `total = 10 - 2 * 3`.
Parentheses are free at run time and priceless during review. When a line mixes three or more operators, or when a colleague might read it two ways, add the brackets that state your intent rather than trusting the table.
Worked examples with real numbers
These four calculations cover billing, science and scores. Type them into a REPL and check each printed line.
>>> bill = 45.50
>>> tip = bill * (15 / 100)
>>> tip
6.825
>>> total = bill + tip
>>> f"{total:.2f}"
'52.33'The tip is a float because 15 / 100 is true division, and the format spec
rounds only the display to two places. The temperature conversion of 25 gives
(25 * 9 / 5) + 32, which is 77.0, and the reverse direction gives 25.0 back.
>>> number = 7
>>> number % 2 == 0
False
>>> weight, height = 70, 1.75
>>> round(weight / height ** 2, 1)
22.9The even check relies on the remainder, and the body mass index relies on the power running before the division.
7. Modern Python and the Operator Landscape
2026 Updates (as of November 2026)
What changed recently, and what did not. Every line below was checked against a primary source in November 2026:
- Python 3.15.2 is the current stable release, shipped on 2026-10-25 after 3.15.0 on 2026-10-09. The operator syntax here is unchanged in 3.15.
- Python 3.10 reached end of life on 2026-10-01, so a beginner should start a project on a supported series such as 3.15.
- Error messages keep improving. Python 3.15 suggests the likely fix for
common mistakes, for example it points from
[1, 2, 3].push(4)to.append, which shortens the search for a wrong name.
Bitwise operators are a short aside. They work on the binary digits of ints:
& is AND, | is OR, ^ is XOR, ~ flips bits, and << and >> shift.
>>> 5 & 3, 5 | 3, 5 ^ 3
(1, 7, 6)
>>> ~5, 1 << 3, 16 >> 2
(-6, 8, 4)Real-World Case Studies
Operator rules look academic until a codebase reaches a scale where every expression runs billions of times. Three public engineering stories show why the details in this lesson matter in production.
Meta, Instagram and Threads. Meta's engineering blog described Python in June 2026 as its most used language, naming the Instagram and Threads backends and the PyTorch origins of its machine learning work. At that scale, a guard such as a membership test or a chained comparison sits on a hot path, and the difference between an operand and a bool can change what a feature flag does.
Netflix. The Netflix Tech Blog described Python across the full content lifecycle in April 2019, including tooling for the Open Connect CDN. Arithmetic and comparison expressions decide encoding choices and capacity checks there, so a floor division that rounds the wrong way is a real operational risk rather than a quiz answer.
Dropbox. Dropbox described one of the largest Python 2 to Python 3
migrations on its engineering blog, and later reported type checking close to
four million lines. A migration at that size has to preserve the exact result
type of every arithmetic expression, which is why the difference between /
and // belongs in every review checklist.
Large teams survive these traps with tests and type checkers, not with memory. A test that pins the expected value and type of a calculation catches a precedence change before a customer sees a four euro receipt.
Practice Questions
Answer these before moving on. Each stem stands on its own, so a review session can show it without the page. The inline questions count too, and every id stays stable for scheduling.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A script runs `seats = int(7.9)`. Which value is stored, and which rule explains it?
How to answer
Multiple-choice question with 2 options. Select one option, then choose Check Answer.
The expression `0.1 + 0.2 == 0.3` evaluates to `True` in Python, because arithmetic on small decimal numbers is exact.
What Comes Next
You can now read the receipt bug, explain the wrong total and repair it with parentheses. The next lesson puts these expressions to work in conditions, where a comparison or a truthy value decides which branch runs. Before that, the video below is a parallel reference, and its operators chapter restates this material in a second voice.
Description
A video walkthrough that supports the section What Comes Next and repeats its main ideas in a second voice.
Dig deeper
- Expressions reference, including the full precedence tablePython 3.15 documentation
- Built-in types, numeric operations and truth value testingPython 3.15 documentation
- The official tutorial introduction to numbers and expressionsPython 3.15 documentation
- PEP 8, the style guide for spaces around operatorsPEP index
- PEP 572, assignment expressions and the walrus operatorPEP index
- PEP 465, the matrix multiplication operatorPEP index
Key Takeaways:
- An expression is a piece of code that evaluates to one value, and its operators decide both the value and the result type.
/is true division and always returns a float, even for6 / 3, which gives2.0.//floors toward negative infinity, so-7 // 2is-4, while%follows the sign of the divisor.divmod(a, b)returns the quotient and remainder pair, sodivmod(47, 5)is(9, 2).**groups right to left and binds tighter than unary minus, so2 ** 3 ** 2is512and-2 ** 2is-4.- Comparison chains mean and, so
18 <= age <= 65tests both bounds and evaluates the middle value once. ==compares values whileiscompares identity, andNoneis the one case whereisis the clear choice.andandorshort circuit and return an operand, so3 and 4is4and[] or 5is5.- Augmented assignment rebinds a name, and
/=turns an int into a float, which changes every later calculation. - Parentheses beat every precedence argument, and they turn a four euro receipt bug into a 24 euro fix.
References
Every factual claim links to its source, with the access date printed so the source can be rechecked.