Operators
Python's operators cover arithmetic, comparison, assignment, logical, identity, membership, and bitwise operations — plus the matrix-multiplication operator. This topic surveys every category with concrete examples.
Operators are the symbols and keywords that combine operands into expressions: + for addition, == for equality, and for logical conjunction, is for identity, in for membership, and more. Python groups its operators into several families, each covered here with its core behavior and a runnable example. Fine-grained rules about precedence and evaluation order are covered separately in Operator Precedence & Evaluation.
Arithmetic Operators
The standard arithmetic operators perform numeric computation: + addition, - subtraction, * multiplication, / true division (always returns a float), // floor division (rounds toward negative infinity), % modulo (remainder), and ** exponentiation.
a, b = 17, 5
print(a + b) # 22
print(a - b) # 12
print(a * b) # 85
print(a / b) # 3.4 -- true division, always float
print(a // b) # 3 -- floor division
print(a % b) # 2 -- remainder
print(a ** b) # 1419857 -- exponentiation
print(-7 // 2) # -4 -- floors toward negative infinity, not toward zero
print(-7 % 2) # 1 -- sign follows the divisorComparison Operators
Comparison operators test the relationship between two values and always produce a bool: == equal, != not equal, < less than, > greater than, <= less than or equal, >= greater than or equal.
print(3 == 3.0) # True -- numeric equality across int/float
print(3 != 4) # True
print(2 < 5) # True
print("abc" < "abd") # True -- strings compare lexicographically
print([1, 2] <= [1, 2, 0]) # True -- lists compare element-wise, then by lengthAssignment and Augmented Assignment
= binds a name to an object. Augmented assignment operators (+=, -=, *=, /=, //=, %=, **=, &=, |=, ^=, <<=, >>=) combine an operation with assignment. For mutable types with an in-place special method (like list's __iadd__), the augmented form mutates the object; for immutable types, it's shorthand for reassignment.
x = 10
x += 5 # equivalent to x = x + 5 for an int (immutable -- rebinds x)
print(x) # 15
lst = [1, 2]
lst += [3] # for a list, += calls __iadd__ and mutates IN PLACE
print(lst) # [1, 2, 3]
original = lst
lst += [4]
print(original is lst) # True -- same object, mutated in place, not reassignedLogical Operators
and, or, and not implement boolean logic, but and/or in Python don't necessarily return True/False — they return one of their actual operands (short-circuit evaluation), which is covered fully in Operator Precedence & Evaluation. not always returns a genuine bool.
print(True and False) # False
print(True or False) # True
print(not True) # False
# and/or return an operand, not necessarily a bool:
print(0 or "default") # 'default' -- 0 is falsy, so 'or' returns the second operand
print("value" and 42) # 42 -- 'value' is truthy, so 'and' returns the second operandIdentity Operators
is and is not test object identity — whether two names refer to the exact same object — as opposed to ==/!=, which test value equality. Covered in depth in Python Object Model; the essential rule is: use is for singleton checks (x is None), and == for everything else.
a = [1, 2]
b = a
c = [1, 2]
print(a is b) # True -- same object
print(a is c) # False -- equal value, different object
print(a is not c) # TrueMembership Operators
in and not in test whether a value appears within a container (or, for a custom type, whether its __contains__ returns True). They work on strings (substring test), lists, tuples, sets, and dicts (tests keys by default).
print("ell" in "hello") # True -- substring test
print(3 in [1, 2, 3]) # True
print(3 not in {1, 2, 4}) # True
print("a" in {"a": 1, "b": 2}) # True -- tests dict KEYS by defaultBitwise Operators
Bitwise operators act on the binary representation of integers: & AND, | OR, ^ XOR, ~ NOT (bitwise complement), plus the shift operators << and >>.
a, b = 0b1100, 0b1010 # 12 and 10
print(bin(a & b)) # 0b1000 -- AND
print(bin(a | b)) # 0b1110 -- OR
print(bin(a ^ b)) # 0b0110 -- XOR
print(~a) # -13 -- bitwise NOT: ~x == -x - 1Shift Operators
<< shifts bits left (equivalent to multiplying by 2 per shifted position) and >> shifts bits right (equivalent to floor-dividing by 2 per position, for non-negative ints). Because Python ints have arbitrary precision, left shifts simply grow the number rather than overflowing or wrapping.
print(1 << 4) # 16 -- 1 * 2**4
print(20 >> 2) # 5 -- 20 // 2**2
print(1 << 100) # a huge number -- no overflow, int grows arbitrarilyMatrix Multiplication Operator (@)
@ is a dedicated operator for matrix multiplication, introduced in Python 3.5 (PEP 465). Python's built-in types don't implement __matmul__ themselves; @ is meant for numeric/array libraries (most commonly the third-party library NumPy) that define matrix multiplication semantics.
In practice, @ is used almost exclusively with NumPy arrays (a third-party library) for real matrix math: array_a @ array_b.
class Matrix2x2:
def __init__(self, rows):
self.rows = rows
def __matmul__(self, other):
a, b = self.rows
c, d = other.rows
return Matrix2x2([
[a[0]*c[0] + a[1]*d[0], a[0]*c[1] + a[1]*d[1]],
[b[0]*c[0] + b[1]*d[0], b[0]*c[1] + b[1]*d[1]],
])
m1 = Matrix2x2([[1, 2], [3, 4]])
m2 = Matrix2x2([[5, 6], [7, 8]])
result = m1 @ m2
print(result.rows)Conditional Expressions (the Ternary Operator)
Python's conditional expression — value_if_true if condition else value_if_false — is a single expression that evaluates to one of two values based on a condition. It is not technically classified as an operator symbol, but it fits naturally alongside the other operator categories since it combines operands using keyword syntax.
age = 20
status = "adult" if age >= 18 else "minor"
print(status) # 'adult'
# Useful inline in other expressions:
values = [1, -2, 3, -4]
abs_values = [v if v >= 0 else -v for v in values]
print(abs_values) # [1, 2, 3, 4]Quick Reference
Arithmetic: + - * / // % **
Comparison: == != < > <= >=
Assignment: = += -= *= /= //= %= **= &= |= ^= <<= >>=
Logical: and or not
Identity: is is not
Membership: in not in
Bitwise: & | ^ ~ << >>
Matrix mult: @
Conditional: x if cond else yKey Takeaways
Arithmetic operators include floor division (//) and true division (/), which behave differently — // always floors toward negative infinity.
Augmented assignment mutates in place for mutable types with __iadd__-style methods, but rebinds the name for immutable types.
and/or return one of their actual operands via short-circuit evaluation, not necessarily True/False; not always returns a genuine bool.
is/is not test identity; ==/!= test value equality — they answer different questions and are not interchangeable.
in/not in test membership and, for dicts, check keys by default.
@ is the matrix multiplication operator, meaningful only for types (typically third-party libraries like NumPy) that implement __matmul__.
if password == None: ...
if password is None: ...
Identity comparison against the None singleton should use is, not ==, both for idiomatic clarity and to avoid depending on a custom __eq__ that might behave unexpectedly with None.
average = total / count # if count and total are both ints and you expected // behavior average = total // count # if you specifically want floor (integer) division # or, if you want a true fractional average, '/' (as above) is already correct -- know which one you mean
/ always performs true division and returns a float, even for two ints; // performs floor division. Confusing the two produces either an unwanted float or an unwanted truncation depending on intent.
What is the difference between / and // in Python?
/ is true division and always returns a float; // is floor division and rounds the result toward negative infinity, returning an int if both operands are ints.
Why does -7 // 2 equal -4 instead of -3?
Floor division rounds toward negative infinity, not toward zero; -3.5 floored is -4, not -3 (which would be truncation toward zero).
What does 0 or "default" evaluate to, and why isn't it simply True or False?
'default' -- Python's 'or' returns one of its actual operands via short-circuit evaluation (the first truthy one, or the last one if none are truthy), not a boolean.
What is the difference between is and ==?
is tests object identity (are they literally the same object); == tests value equality via __eq__ (do they represent the same value).
What does in test when used with a dictionary?
By default, in tests membership among the dictionary's KEYS, not its values.
What does the @ operator do, and on what types does it work?
@ invokes matrix multiplication via a type's __matmul__ method; Python's built-in types don't define it, so @ is mainly used with array/matrix libraries such as the third-party NumPy.
For a list, does lst += [x] rebind lst to a new list or mutate the existing one?
It mutates the existing list in place (via list's __iadd__), keeping the same object identity -- unlike int += which always rebinds since ints are immutable.
Write a function clamp(value, low, high) using conditional expressions that returns value clamped to the [low, high] range.
def clamp(value, low, high): return low if value < low else high if value > high else value print(clamp(15, 0, 10)) # 10 print(clamp(-3, 0, 10)) # 0 print(clamp(5, 0, 10)) # 5
Chained conditional expressions check the lower bound first, then the upper bound, falling through to the original value if it's already in range.
What does this code print?
[1, 2, 3, 4] [1, 2, 3, 4] True False -- '+=' on a list mutates in place so b (same object as a) sees the change too; 'a + [5]' creates a brand NEW list, so c is a different object from a.
Write code that uses bitwise operators to check whether an integer n is even, without using the modulo operator.
def is_even(n): return (n & 1) == 0 print(is_even(4)) # True print(is_even(7)) # False n & 1 isolates the least-significant bit; that bit is 0 for even numbers and 1 for odd numbers, so comparing it to 0 tests evenness.
a = [1, 2, 3]
b = a
a += [4]
c = a + [5]
print(a, b, a is b, c is a)