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 divisor

Comparison 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 length

Assignment 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 reassigned

Logical 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 operand

Identity 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)      # True

Membership 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 default

Bitwise 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 - 1

Shift 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 arbitrarily

Matrix 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 y

Key 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)