Python operators: arithmetic, comparison, logical, bitwise, assignment and more
Advertisement
Operators are the symbols that combine values: +, ==, and, in. Almost every line of Python uses them, and a few have surprising behaviour: floor division with negative numbers, and returning a value rather than True, is versus ==. This post goes through them group by group with output from real runs.
Arithmetic operators
print(7 + 2, 7 - 2, 7 * 2, 7 / 2, 7 // 2, 7 % 2, 2 ** 3)
# 9 5 14 3.5 3 1 8
| Operator | Meaning | Note |
|---|---|---|
+ - * |
add, subtract, multiply | + and * also join and repeat strings and lists |
/ |
true division | always returns a float |
// |
floor division | rounds down toward negative infinity |
% |
remainder (modulo) | the sign follows the divisor |
** |
power | right to left: 2 ** 3 ** 2 is 2 ** 9 |
Negative numbers show the difference between truncating and flooring:
print(-7 // 2, -7 % 3, 7 % -3, divmod(-7, 3)) # -4 2 -2 (-3, 2)
print(2 ** -1, 10 ** 20, 0.1 * 3) # 0.5 100000000000000000000 0.30000000000000004
a == (a // b) * b + a % b always holds, which is why -7 // 2 is -4 and not -3. Integers have no size limit (10 ** 20 is exact), while floats are approximate. divmod(a, b) returns the quotient and remainder together. Division by zero raises ZeroDivisionError.
Comparison (relational) operators
<, <=, >, >=, ==, != return True or False.
print(3 > 2, 3 < 2, 3 >= 3, 3 <= 2, 3 == 3.0, 3 != 4, "a" == "a", "10" == 10)
# True False True False True True True False
3 == 3.0 is true (numbers compare by value), while "10" == 10 is false: a string is never equal to a number. Strings compare by character code, lists and tuples compare item by item.
Python allows chained comparisons, which read like mathematics:
print(1 < 2 < 3, 1 < 3 < 2) # True False
1 < x < 10 is clearer and safer than 1 < x and x < 10.
Equality versus identity
== asks "are the values equal?" is asks "are they the same object?"
a = [1, 2]; b = [1, 2]; c = a
print(a == b, a is b, a is c, a is not b) # True False True True
Use is only for None (if x is None) and for sentinel objects. For numbers and strings, is can seem to work (small integers are shared, 256 is 256) and then fail for no obvious reason, so use ==.
Logical operators
and, or, not. They work on truthiness, and and / or return one of their operands, not necessarily True or False.
print(True and False, True or False, not True) # False True False
print(0 or "x", "" or "y", 0 and 5, 5 and 6, None or [])
# x y 0 6 []
The rule: a or b returns a if a is truthy, else b. a and b returns a if a is falsy, else b. Evaluation short-circuits, so the right side runs only if needed:
print((0 and 1) or 2, 1 and 2 or 3) # 2 2
This allows idioms such as name = user_input or "Guest", and guards such as if items and items[0] > 5: (the index is never evaluated for an empty list). Be careful with value or default when 0 or "" are valid values, since they are falsy. not always returns a real boolean.
Bitwise operators
Bitwise operators work on the binary digits of integers.
print(5 & 3, 5 | 3, 5 ^ 3, ~5, 5 << 2, 5 >> 1, -5 >> 1)
# 1 7 6 -6 20 2 -3
print(bin(5 & 3), bin(5 | 3), bin(5 ^ 3)) # 0b1 0b111 0b110
| Operator | Name | Example (5 = 101, 3 = 011) |
|---|---|---|
& |
AND (1 where both are 1) | 101 & 011 = 001 → 1 |
| |
OR (1 where either is 1) | 101 | 011 = 111 → 7 |
^ |
XOR (1 where they differ) | 101 ^ 011 = 110 → 6 |
~ |
NOT (flip all bits) | ~x == -x - 1 |
<< |
shift left | x << n is x * 2**n |
>> |
shift right | x >> n is x // 2**n |
~5 is -6 because Python integers are signed with unlimited width: ~x equals -(x + 1). To see a fixed width, mask: format(~5 & 0xFF, "08b") gives 11111010. Practical uses: flags and permissions, checking odd or even (n & 1), fast multiplication or division by powers of 2, and low-level protocols.
n = 15
print(n & 1 == 1, "odd" if n & 1 else "even") # True odd
print(1 << 10, 0b1111 & 0b0101) # 1024 5
Assignment operators
= binds a name; the augmented forms combine an operation with assignment: +=, -=, *=, /=, //=, %=, **=, &=, |=, ^=, <<=, >>=.
x = 5; x += 2; x -= 1; x *= 3; x //= 4; x **= 2; x %= 5
print(x) # 1 (5 -> 7 -> 6 -> 18 -> 4 -> 16 -> 1)
y = 7; y <<= 1; y >>= 2; y &= 2; y |= 4; y ^= 1
print(y) # 7
There is no ++ or -- in Python; write x += 1. Multiple assignment and swapping use tuple unpacking: a, b = b, a.
For mutable objects, += changes the object in place (lst += [3] extends the list), while lst = lst + [3] creates a new one. This difference matters when two names share the list.
Ternary (conditional) operator
print("yes" if 5 > 3 else "no") # yes
print((lambda a, b: a if a > b else b)(3, 9)) # 9
The form is value_if_true if condition else value_if_false. Do not nest it more than once; use if statements instead.
Identity and membership operators
is,is not: same object or not (above).in,not in: membership in a sequence, set or dictionary (keys).
print("a" in "cat", 3 not in [1, 2], "k" in {"k": 1}, 1 in {1}, 2 in (1, 2))
# True True True True True
For strings, in tests for a substring. For dictionaries it tests keys. For large collections, sets and dictionaries test membership much faster than lists (see sets).
Operator precedence
When an expression mixes operators, Python follows a fixed order. From highest to lowest (roughly):
**- unary
+x,-x,~x *,/,//,%+,-<<,>>&, then^, then|- comparisons,
is,in(all the same level, and chainable) notandor- conditional
if else - assignment
print(1 + 2 * 3, (1 + 2) * 3, 2 ** 3 ** 2, -2 ** 2, not 1 == 2, 10 - 4 - 3, 100 / 10 / 5)
# 7 9 512 -4 True 3 2.0
Note -2 ** 2 is -4 (the power binds tighter than the minus) and 10 - 4 - 3 is 3 (left to right). When in doubt, use brackets: they cost nothing and make the intent clear.
The math module
Operators cover basic arithmetic. For more, import math:
import math
print(math.sqrt(25), math.ceil(4.1), math.floor(-4.1), math.trunc(-4.9), math.pi)
# 5.0 5 -5 -4 3.141592653589793
print(math.log(math.e), math.log10(1000), math.log2(8), math.hypot(3, 4), math.degrees(math.pi))
# 1.0 3.0 3.0 5.0 180.0
print(math.gcd(12, 18), math.factorial(5), math.isclose(0.1 + 0.2, 0.3), math.isnan(float("nan")))
# 6 120 True True
Two reminders. round() uses banker's rounding (half goes to the even number): round(4.5) is 4 and round(5.5) is 6. And comparing floats needs care:
print(0.1 + 0.2 == 0.3, round(0.1 + 0.2, 2) == 0.3, math.isclose(0.1 + 0.2, 0.3))
# False True True
Common mistakes
- Using
=where==is meant (Python reports a syntax error in conditions). - Expecting
//to truncate negative numbers toward zero. - Using
isto compare numbers or strings. - Expecting
and/orto returnTrue/False. - Writing
x++. - Forgetting that
^is XOR, not power (**). - Comparing floats exactly.
- Not parenthesising mixed
and/orexpressions.
Practice
Write a function that tells whether a year is a leap year using only %, and, or (a year is a leap year if divisible by 4, except century years unless divisible by 400). Then use & to test whether a permission value has the "write" bit (value 2) set. Finally, predict the output of print(2 + 3 * 4 ** 2 // 5 % 3) before running it.
Continue
These operators act on the types in the fundamentals post; next, see how strings and tuples use them. For a quick percentage calculation outside code, try the Percentage Calculator, and see the Python cheat sheet for a summary.