Operators and expressions
Almost everything a program does comes down to two activities: working out a value and doing something with it. This lesson is about the first one. An operator is a small symbol — like + or < — that takes one or more values and produces a new value. String enough operators together and you have an expression: a recipe Python follows to arrive at a single answer. By the end of this page you will be able to read any line of arithmetic, comparison, or logic and predict, on paper, exactly what value it produces.
Expression vs. statement
These two words are the foundation, so let us pin them down precisely.
- An expression is any piece of code that computes to a value. You can always ask "what does this equal?" of an expression and get an answer back.
2 + 3is an expression — it computes to5. A bare value like"hi"is an expression (it computes to itself). A variable name likeageis an expression (it computes to whatever is stored inside it). - A statement is a complete instruction that does something rather than producing a value you can use.
x = 5is a statement: it stores 5 inx. Asking "what doesx = 5equal?" makes no sense — it is an action, not a value.
The line between them is real. You can put an expression wherever a value is expected — inside a print, on the right of an =, as an argument. You cannot put a statement there.
total = 2 + 3 # statement (assignment); its right side, 2 + 3, is an expression
print(2 + 3) # the expression 2 + 3 computes to 5, then print does something with it -> 5
print(total) # the variable name "total" is an expression that computes to 5 -> 5
:::note Quick vocabulary
The values an operator works on are called operands. In 2 + 3, the + is the operator and 2 and 3 are the operands. That is all "operand" means — no need to overthink it. When Python takes an expression and figures out its value, we say it evaluates the expression.
:::
Arithmetic operators
These do ordinary math. Most look exactly like the symbols you already know — addition, subtraction, multiplication — but a few behave in ways that surprise newcomers, so we will walk through every one with its output.
print(7 + 2) # 9 add
print(7 - 2) # 5 subtract
print(7 * 2) # 14 multiply (the asterisk is "times")
The two kinds of division
Division is where Python splits into two separate operators, and the difference matters constantly. A true division with a single slash / answers "what is the exact share?" and always hands back a decimal number (called a float) — even when the division comes out clean. An integer (floor) division with a double slash // answers a different question: "how many whole times does the bottom fit into the top?" It throws away anything left over and gives a whole number.
print(7 / 2) # 3.5 true division: the exact answer, with a decimal point
print(7 // 2) # 3 floor division: how many whole 2s fit in 7 (the .5 is dropped)
print(6 / 2) # 3.0 STILL a decimal! / always returns a float, even for clean divisions
print(6 // 2) # 3 floor division of a clean case gives a plain whole number
The word "floor" means "round down to the nearest whole number." For positive numbers this just means "chop off the decimal part," which is the intuition to carry around. So 7 // 2 is 3 because three whole 2s fit inside 7 (that uses up 6), and the leftover 1 is discarded.
Modulo: the leftover operator
The modulo operator % is the partner of floor division. Where // keeps the whole part and throws away the leftover, % keeps only the leftover — the remainder after dividing as many whole times as possible.
print(7 % 2) # 1 7 = (3 whole 2s = 6) + 1 left over, so the remainder is 1
print(10 % 3) # 1 10 = (3 whole 3s = 9) + 1 left over
print(12 % 4) # 0 4 divides 12 evenly, nothing left over
Two mental pictures make modulo click. The clock: a 12-hour clock is really just % 12 arithmetic. Five hours after 10 o'clock is not 15 o'clock — it wraps around to 3, because 15 % 12 == 3. Modulo is the math of things that wrap around in a circle. Grouping: if you line 7 people up in pairs, you get 3 full pairs (7 // 2) with 1 person standing alone (7 % 2). The // counts the full groups; the % counts the leftovers.
:::tip The even / odd test
The single most common use of % is the even / odd test. A number is even exactly when dividing it by 2 leaves nothing over. So n % 2 == 0 is True for even numbers and False for odd ones — because odd numbers always leave a remainder of 1. For example 4 % 2 is 0 (even), while 5 % 2 is 1 (odd). You will reach for this trick constantly.
:::
Power
The double asterisk ** is exponentiation — raising a number to a power, i.e. multiplying it by itself a given number of times.
print(2 ** 3) # 8 2 to the power of 3 = 2 * 2 * 2
print(5 ** 2) # 25 5 squared = 5 * 5
print(10 ** 0) # 1 anything to the power 0 is 1
Operator precedence: who goes first
When several operators appear in one expression, Python does not just read left to right. It follows precedence — a fixed pecking order, exactly like the "order of operations" from math class. Higher-precedence operators run before lower ones. For arithmetic the order is:
**(power) — runs first*///%(multiply, both divisions, modulo) — run next+-(add, subtract) — run last
Here is a worked example where it changes the answer. Consider 2 + 3 * 4. Because * outranks +, Python evaluates the multiplication first:
print(2 + 3 * 4) # step 1: 3 * 4 = 12 step 2: 2 + 12 = 14 -> 14 (NOT 20)
print((2 + 3) * 4) # parentheses force the add first: 5 * 4 = 20 -> 20
print(2 ** 3 * 2) # power first: 2 ** 3 = 8, then 8 * 2 = 16 -> 16
:::tip Use parentheses when unsure
Parentheses ( ) always win — Python evaluates whatever is inside them first, before any operator outside. You never have to memorise the whole precedence table if you simply add parentheses to spell out your intent. (2 + 3) * 4 reads clearly and is never wrong. Clarity for the next reader (often future-you) beats cleverness.
:::
Comparison operators
Where arithmetic operators produce numbers, comparison operators ask a question and answer with a Boolean — one of exactly two values, True or False. (The name "Boolean" honours the mathematician George Boole; just read it as "yes/no value.") There are six comparisons:
==— "are these equal?"!=— "are these not equal?" (the!means "not")<— "is the left less than the right?">— "is the left greater than the right?"<=— "less than or equal to?">=— "greater than or equal to?"
print(5 == 5) # True 5 equals 5
print(5 == 3) # False 5 does not equal 3
print(5 != 3) # True 5 is not 3, so "not equal?" is True
print(3 < 5) # True 3 is less than 5
print(5 > 5) # False 5 is not strictly greater than 5
print(5 >= 5) # True 5 is greater-than-OR-equal-to 5
print(5 <= 4) # False 5 is neither less than nor equal to 4
The big one: == vs =
This is the single most common beginner bug, so we will dwell on it. The two symbols look nearly identical but do opposite kinds of work:
- A single
=assigns. It is a statement: it stores the value on its right into the variable on its left.x = 5means "put 5 into the box named x." It does not ask a question and it does not hand back True or False. - A double
==compares. It is an expression: it asks "are these two values equal?" and computes to a Boolean.x == 5means "is whatever is in x equal to 5?" and gives backTrueorFalse.
x = 5 # ASSIGN: store 5 in x. This is an action, not a question.
print(x) # 5 x now holds 5
print(x == 5) # True COMPARE: is x equal to 5? Yes.
print(x == 9) # False is x equal to 9? No.
Here is what goes wrong when you reach for = while meaning ==. Suppose you want to check whether a password attempt matches, but you slip and type a single =:
# WRONG — you meant to compare, but you typed a single =
secret = "open"
if secret = "guess": # SyntaxError: Python refuses to run this
print("match")
In Python this particular slip is caught for you — assignment is a statement and is not allowed inside the question part of an if, so Python raises a SyntaxError instead of silently misbehaving. That error message is doing you a favour. The fix is always the same: when you want to test for equality, use the double ==.
# RIGHT — compare with ==
secret = "open"
if secret == "guess": # asks: does secret equal "guess"? (False here)
print("match")
else:
print("no match") # -> no match
Boolean operators: and, or, not
Once you can produce True/False values, you will want to combine them. The three Boolean operators do exactly that. The one-line intuition: and = both, or = either, not = flip.
andisTrueonly when both sides are True. If either side is False, the whole thing is False.orisTruewhen at least one side is True. It is False only when both sides are False.nottakes a single value and flips it:not TrueisFalse,not FalseisTrue.
print(True and True) # True both sides True
print(True and False) # False one side False is enough to fail "and"
print(False or True) # True "or" needs only one True side
print(False or False) # False both sides False
print(not True) # False flip
In real code you rarely write the bare words True and False — you feed in comparisons, each of which computes to a Boolean first:
age = 20
print(age > 18 and age < 65) # True 20>18 is True AND 20<65 is True -> True
print(age < 13 or age > 18) # True 20<13 is False, but 20>18 is True -> True
print(not age == 20) # False age == 20 is True, and "not" flips it to False
:::note Short-circuiting (just so you have heard the word)
Python is lazy with and and or. For and, if the left side is already False the whole result must be False, so Python does not even bother evaluating the right side. For or, if the left side is already True it stops there. This is mostly invisible, but it is why an and can guard a risky check on its right — the right side only runs when the left side allows it.
:::
Combining comparisons (range checks)
A classic job for and is asking whether a number falls inside a range. To test "is x at least 0 and below 10?" you write two comparisons joined by and:
x = 7
print(0 <= x and x < 10) # True 0<=7 is True AND 7<10 is True
Python offers a tidy shorthand here that most languages do not have: you can chain the comparisons and write the range the way you would in math.
print(0 <= x < 10) # True reads "0 <= x < 10" — same meaning as the line above
Both forms mean exactly the same thing; the chained version is just easier to read. Use whichever is clearer to you.
String operators
A pleasant surprise: two arithmetic symbols also work on text (strings), with sensible meanings. + concatenates — it glues two strings end to end into one longer string. * repeats — it copies a string a whole number of times.
print("foo" + "bar") # foobar glued together (no space is added for you)
print("hi " + "there") # hi there the space is part of the first string
print("ab" * 3) # ababab "ab" repeated 3 times
print("-" * 10) # ---------- handy for drawing a line
But there is a hard limit: you cannot add a string and a number. Python will not guess what you mean, so it raises a TypeError rather than do something surprising:
print("age: " + 20) # TypeError: can only concatenate str to str
print("age: " + str(20)) # age: 20 convert the number to text first with str(...)
The fix shown above — wrapping the number in str(...) to turn it into text — is one common way to mix the two. You will meet a neater way (f-strings) in the strings lesson.
Putting it together: step-by-step evaluation
The real skill is reading a busy expression and evaluating it the way Python does — innermost and highest-precedence parts first, one step at a time. Two worked traces:
# Expression: 2 + 3 * 4 > 10
# step 1 multiply first (higher precedence): 3 * 4 = 12 -> 2 + 12 > 10
# step 2 add next: 2 + 12 = 14 -> 14 > 10
# step 3 compare last: 14 > 10 -> True
print(2 + 3 * 4 > 10) # True
# Expression: 10 % 3 == 1 and 4 // 2 == 2
# step 1 left modulo: 10 % 3 = 1 -> 1 == 1 and 4 // 2 == 2
# step 2 left compare: 1 == 1 = True -> True and 4 // 2 == 2
# step 3 right divide: 4 // 2 = 2 -> True and 2 == 2
# step 4 right compare: 2 == 2 = True -> True and True
# step 5 combine: True and True -> True
print(10 % 3 == 1 and 4 // 2 == 2) # True
Notice the overall ordering across categories: arithmetic happens first, then comparisons, then the Boolean and/or combine the results. If that order is ever murky in your head, you already know the cure — add parentheses to make it explicit.
:::note Looking ahead
Comparison and Boolean expressions are the fuel for conditionals. An if-statement is nothing more than "evaluate this True/False expression, and if it is True, run this block." Everything you just practised — n % 2 == 0, 0 <= x < 10, age > 18 and has_id — is exactly the kind of question you will place after the word if in the next lesson.
:::
Why it matters
An expression computes to a value; a statement performs an action. Use / for an exact (decimal) divide and // for a whole-number divide; use % for the remainder (and the even check n % 2 == 0). Compare with == and assign with = — never confuse them. Combine truths with and/or/not, and reach for parentheses whenever the order of operations is not obvious.
Where this leads: operators are syntax; next comes fluency — the idioms real codebases and interviewers expect. See Where this leads next.
Checkpoint
Operators and expressions
Pass to unlock the Next button belowNext: Conditionals →