Python Operators — Complete Reference
Operators are special symbols that perform operations on variables and values. Python provides a rich set of operators for different purposes. This guide covers every operator type with practical examples.
Learning Objectives
- Understand and use all seven categories of Python operators
- Know the difference between
==andis - Master short-circuit evaluation with logical operators
- Use bitwise operators for real-world tasks like permissions and flags
- Recognize common operator pitfalls and how to avoid them
Arithmetic Operators
Arithmetic operators perform mathematical calculations.
| Operator | Name | Example | Result |
|---|---|---|---|
+ | Addition | 5 + 3 | 8 |
- | Subtraction | 5 - 3 | 2 |
* | Multiplication | 5 * 3 | 15 |
/ | Division | 5 / 3 | 1.6667 |
// | Floor Division | 5 // 3 | 1 |
% | Modulo | 5 % 3 | 2 |
** | Power | 5 ** 3 | 125 |
Division in Python 3 vs Python 2
In Python 3, / always returns a float:
# Python 3
>>> 10 / 4
2.5
>>> 10 / 2
5.0 # Still a float!
# Python 2 (legacy)
>>> 10 / 4
2 # Integer division (truncated)
Use // when you want integer (floor) division in Python 3:
>>> 10 // 4
2
>>> -10 // 4
-3 # Floors toward negative infinity
Modulo with Negative Numbers
The modulo operator in Python always returns a result with the same sign as the divisor:
>>> 7 % 3
1
>>> -7 % 3
2 # Not -1! Python adjusts to keep result positive
>>> 7 % -3
-2 # Result takes sign of divisor
>>> -7 % -3
-1
This differs from many other languages and can surprise beginners.
Comparison (Relational) Operators
Comparison operators return True or False based on the comparison.
| Operator | Meaning | Example | Result |
|---|---|---|---|
== | Equal to | 5 == 5 | True |
!= | Not equal to | 5 != 3 | True |
> | Greater than | 5 > 3 | True |
< | Less than | 5 < 3 | False |
>= | Greater or equal | 5 >= 5 | True |
<= | Less or equal | 5 <= 3 | False |
Chained Comparisons
Python supports chaining multiple comparisons in a single expression:
>>> x = 5
>>> 1 < x < 10
True
>>> 1 < x < 3
False
# Equivalent to:
>>> 1 < x and x < 10
True
# You can chain even more:
>>> 1 < x < 100 > 50
True
This is more readable and often more efficient than using and.
== vs is for None
This is a critical distinction:
>>> x = None
>>> x == None
True
>>> x is None
True
# But be careful:
>>> a = float('nan')
>>> a == a
False # NaN is never equal to itself
>>> a is a
True # Identity check still works
Always use is to compare with None, True, or False:
# Correct
if x is None:
pass
# Incorrect (works but not Pythonic)
if x == None:
pass
Floating Point Comparison Issues
Floating point arithmetic can produce unexpected results:
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
Use math.isclose() for float comparisons:
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
# With custom tolerance
>>> math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9)
True
Assignment Operators
Assignment operators assign values to variables.
| Operator | Equivalent | Example |
|---|---|---|
= | x = 5 | x = 5 |
+= | x = x + 5 | x += 5 |
-= | x = x - 5 | x -= 5 |
*= | x = x * 5 | x *= 5 |
/= | x = x / 5 | x /= 5 |
//= | x = x // 5 | x //= 5 |
%= | x = x % 5 | x %= 5 |
**= | x = x ** 5 | x **= 5 |
Augmented Assignment with Mutable Types
Be careful with mutable objects:
>>> a = [1, 2, 3]
>>> b = a
>>> a += [4, 5] # This calls __iadd__ (in-place add)
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3, 4, 5] # b is affected! Same object
>>> a = [1, 2, 3]
>>> b = a
>>> a = a + [4, 5] # This creates a NEW list
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3] # b is NOT affected
This happens because += calls __iadd__ which modifies in place for lists, while + creates a new object.
Logical Operators
Logical operators combine conditional statements.
| Operator | Description | Example |
|---|---|---|
and | Returns True if both are True | True and False → False |
or | Returns True if at least one is True | True or False → True |
not | Reverses the boolean value | not True → False |
Truth Tables
# and truth table
True and True # True
True and False # False
False and True # False
False and False # False
# or truth table
True or True # True
True or False # True
False or True # True
False or False # False
# not truth table
not True # False
not False # True
Short-Circuit Evaluation
Python evaluates logical operators lazily — it stops as soon as the result is determined:
# 'and' stops at first False
>>> False and print("Never executes")
False
# 'or' stops at first True
>>> True or print("Never executes")
True
Return Values (Not What You Expect)
Logical operators return operands, not necessarily True or False:
>>> 3 and 5
5 # Returns last truthy value
>>> 0 and 5
0 # Returns first falsy value
>>> 3 or 5
3 # Returns first truthy value
>>> 0 or 5
5 # Returns last value if all falsy before
>>> "" or "hello"
'hello'
>>> "hello" or ""
'hello'
The rule:
a and b: Ifais falsy, returna; otherwise returnba or b: Ifais truthy, returna; otherwise returnb
Common Patterns
Default values:
>>> name = "" or "Anonymous"
'Anonymous'
>>> name = "Alice" or "Anonymous"
'Alice'
Guard clauses:
# Safe attribute access
>>> users = {"admin": {"role": "super"}}
>>> role = users.get("admin") and users["admin"].get("role")
'super'
>>> role = users.get("missing") and users["missing"].get("role")
None
Conditional assignment:
>>> x = 5
>>> result = x > 10 and "big" or "small"
>>> result
'small'
Bitwise Operators
Bitwise operators work on integers at the binary level.
| Operator | Name | Example | Result (Binary) |
|---|---|---|---|
& | AND | 5 & 3 | 1 (001) |
| | OR | 5 | 3 | 7 (111) |
^ | XOR | 5 ^ 3 | 6 (110) |
~ | NOT | ~5 | -6 |
<< | Left Shift | 5 << 2 | 20 (10100) |
>> | Right Shift | 5 >> 2 | 1 (1) |
Truth Tables
# AND (&): Both bits must be 1
>>> 0b1010 & 0b1100 # 10 & 12
0b1000 # 8
# OR (|): At least one bit must be 1
>>> 0b1010 | 0b1100 # 10 | 12
0b1110 # 14
# XOR (^): Bits must be different
>>> 0b1010 ^ 0b1100 # 10 ^ 12
0b0110 # 6
# NOT (~): Flips all bits
>>> ~5
-6 # Equivalent to -(n+1)
# Left Shift: Multiply by 2^n
>>> 5 << 2 # 5 * 2^2
20
# Right Shift: Integer divide by 2^n
>>> 20 >> 2 # 20 // 2^2
5
Real-World Uses: Flags and Permissions
Bitwise operators are commonly used for feature flags and permissions:
# Define permission flags
READ = 0b001 # 1
WRITE = 0b010 # 2
EXECUTE = 0b100 # 4
# Combine permissions
user_permissions = READ | WRITE # 0b011 = 3
# Check permissions
has_read = bool(user_permissions & READ) # True
has_execute = bool(user_permissions & EXECUTE) # False
# Add permission
user_permissions |= EXECUTE # 0b111 = 7
# Remove permission
user_permissions &= ~WRITE # 0b101 = 5
# Toggle permission
user_permissions ^= READ # 0b100 = 4
# Check if multiple permissions set
needs = READ | WRITE
has_all = (user_permissions & needs) == needs # False
Membership Operators
Membership operators test if a value is found in a sequence.
| Operator | Description | Example |
|---|---|---|
in | Returns True if value is in sequence | 3 in [1, 2, 3] → True |
not in | Returns True if value is not in sequence | 3 not in [1, 2, 3] → False |
With Different Types
# Lists
>>> 3 in [1, 2, 3, 4, 5]
True
>>> "x" in ["a", "b", "c"]
False
# Strings (substring check)
>>> "py" in "python"
True
>>> "Python" in "python"
False # Case-sensitive!
# Tuples
>>> 10 in (10, 20, 30)
True
# Dictionaries (checks keys, not values!)
>>> "name" in {"name": "Alice", "age": 30}
True
>>> "Alice" in {"name": "Alice", "age": 30}
False # Check values with .values()
# Sets (most efficient membership testing)
>>> 3 in {1, 2, 3, 4, 5}
True
# Range
>>> 5 in range(10)
True
>>> 10 in range(10)
False # range(10) is 0-9
__contains__ Protocol
When you use in, Python calls the __contains__ method:
class Fibonacci:
def __init__(self, limit):
self.limit = limit
self.fibs = [0, 1]
while self.fibs[-1] < limit:
self.fibs.append(self.fibs[-1] + self.fibs[-2])
def __contains__(self, item):
return item in self.fibs
>>> fib = Fibonacci(100)
>>> 21 in fib
True
>>> 22 in fib
False
Identity Operators
Identity operators check if two variables point to the same object in memory.
| Operator | Description | Example |
|---|---|---|
is | True if both reference same object | a is b |
is not | True if they reference different objects | a is not b |
Integer CPython Caching
CPython caches integers from -5 to 256 for performance:
>>> a = 256
>>> b = 256
>>> a is b
True # Same cached object
>>> a = 257
>>> b = 257
>>> a is b
False # Different objects (usually)
# This behavior is implementation-specific and shouldn't be relied upon
String Interning
Python interns (reuses) strings that look like identifiers:
>>> a = "hello"
>>> b = "hello"
>>> a is b
True # Interned
>>> a = "hello world"
>>> b = "hello world"
>>> a is b
True # Also interned (contains only identifier chars)
>>> a = "hello!"
>>> b = "hello!"
>>> a is b
False # Contains non-identifier char, not interned
When to Use is vs ==
Use is for:
- Checking
None:if x is None: - Checking
True/False:if flag is True: - Checking sentinels:
if result is MISSING:
Use == for:
- Everything else (value comparison)
# Correct
if x is None:
pass
if result is not True:
pass
# Incorrect (may fail with custom __eq__)
if x == None: # Works but not Pythonic
pass
Operator Precedence
From highest to lowest:
| Precedence | Operators | Description |
|---|---|---|
| 1 | () | Parentheses (grouping) |
| 2 | ** | Exponentiation |
| 3 | ~, +x, -x | Bitwise NOT, unary plus/minus |
| 4 | *, /, //, % | Multiplication, division, floor div, modulo |
| 5 | +, - | Addition, subtraction |
| 6 | <<, >> | Bitwise shifts |
| 7 | & | Bitwise AND |
| 8 | ^ | Bitwise XOR |
| 9 | | | Bitwise OR |
| 10 | ==, !=, >, <, >=, <=, is, is not, in, not in | Comparisons |
| 11 | not | Logical NOT |
| 12 | and | Logical AND |
| 13 | or | Logical OR |
Parentheses for Clarity
Always use parentheses when precedence is unclear:
# Avoid this
>>> 2 + 3 * 4
14
# Prefer this
>>> 2 + (3 * 4)
14
# Essential with mixed operators
>>> (x > 5) and (y < 10)
# Without parentheses - harder to read
>>> x > 5 and y < 10
Special Operator Behaviors
String Repetition with *
The * operator repeats strings:
>>> "ha" * 3
'hahaha'
>>> "-" * 20
'--------------------'
>>> 3 * "py"
'pypypy'
List Repetition with *
Lists can also be repeated:
>>> [0] * 5
[0, 0, 0, 0, 0]
>>> [1, 2] * 3
[1, 2, 1, 2, 1, 2]
# Be careful with nested lists!
>>> a = [[0]] * 3
>>> a
[[0], [0], [0]]
>>> a[0].append(1)
>>> a
[[0, 1], [0, 1], [0, 1]] # All sublists are the same object!
# Correct way
>>> b = [[0] for _ in range(3)]
>>> b[0].append(1)
>>> b
[[0, 1], [0], [0]] # Independent sublists
Operator Overloading Preview
Python allows custom classes to define operator behavior:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
>>> v1 = Vector(1, 2)
>>> v2 = Vector(3, 4)
>>> v1 + v2
Vector(4, 6)
>>> v1 * 3
Vector(3, 6)
Common Mistakes
Mistake 1: Using = Instead of ==
x = 5
# Wrong: assignment in condition
if x = 5: # SyntaxError
print("yes")
# Correct
if x == 5:
print("yes")
Mistake 2: Chained Comparison with and
# Wrong (looks right but behaves differently)
>>> x = 5
>>> 1 < x and x < 10
True
# This looks similar but isn't:
>>> 1 < x < 10 # Actually equivalent to above (good)
True
# The gotcha: logical operators return operands
>>> 1 and 2 and 3
3
>>> 0 and 2 and 3
0
Mistake 3: Mutable Default with *
# Wrong: shared mutable default
>>> def append_to(item, lst=[]):
... lst.append(item)
... return lst
>>> append_to(1)
[1]
>>> append_to(2)
[1, 2] # Not [2]! List persists between calls
# Correct
>>> def append_to(item, lst=None):
... if lst is None:
... lst = []
... lst.append(item)
... return lst
Mistake 4: Float Comparison with ==
# Wrong
>>> 0.1 + 0.2 == 0.3
False
# Correct
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
Mistake 5: Confusing in with Dictionaries
>>> d = {"name": "Alice", "age": 30}
# Wrong: checking values instead of keys
>>> "Alice" in d
False
# Correct: 'in' checks keys
>>> "name" in d
True
# To check values
>>> "Alice" in d.values()
True
Practice Exercises
Exercise 1: Temperature Converter
Write a function that converts Celsius to Fahrenheit and determines if the water is boiling (>= 100°C / 212°F).
def check_boiling(celsius):
fahrenheit = celsius * 9/5 + 32
is_boiling = celsius >= 100
return fahrenheit, is_boiling
# Test
temp_f, boiling = check_boiling(100)
print(f"{temp_f}°F, Boiling: {boiling}")
# Output: 212.0°F, Boiling: True
temp_f, boiling = check_boiling(25)
print(f"{temp_f}°F, Boiling: {boiling}")
# Output: 77.0°F, Boiling: False
Exercise 2: Permission Checker
Create a function that checks what operations a user can perform based on permission flags.
READ = 1
WRITE = 2
EXECUTE = 4
def check_permissions(user_flags):
can_read = bool(user_flags & READ)
can_write = bool(user_flags & WRITE)
can_execute = bool(user_flags & EXECUTE)
permissions = []
if can_read:
permissions.append("read")
if can_write:
permissions.append("write")
if can_execute:
permissions.append("execute")
return permissions
# Test
print(check_permissions(READ | WRITE)) # ['read', 'write']
print(check_permissions(READ | EXECUTE)) # ['read', 'execute']
print(check_permissions(READ | WRITE | EXECUTE)) # ['read', 'write', 'execute']
print(check_permissions(0)) # []
Exercise 3: Data Validator
Write a validator that checks if data meets multiple conditions using logical operators.
def validate_user(data):
has_name = bool(data.get("name"))
has_email = "@" in data.get("email", "")
age = data.get("age", 0)
valid_age = 18 <= age <= 120
is_valid = has_name and has_email and valid_age
errors = []
if not has_name:
errors.append("Name required")
if not has_email:
errors.append("Valid email required")
if not valid_age:
errors.append("Age must be 18-120")
return is_valid, errors
# Test
valid, errors = validate_user({
"name": "Alice",
"email": "alice@example.com",
"age": 25
})
print(f"Valid: {valid}, Errors: {errors}")
# Output: Valid: True, Errors: []
valid, errors = validate_user({
"name": "",
"email": "invalid",
"age": 15
})
print(f"Valid: {valid}, Errors: {errors}")
# Output: Valid: False, Errors: ['Name required', 'Valid email required', 'Age must be 18-120']
Key Takeaways
- Division:
/always returns a float in Python 3; use//for floor division - Modulo with negatives: Returns result with the same sign as the divisor
- Chained comparisons:
1 < x < 10is cleaner than1 < x and x < 10 ==vsis: UseisforNone/True/Falsechecks;==for value comparison- Logical operators return operands:
0 or "hello"returns"hello", notTrue - Short-circuit evaluation:
andstops at first falsy;orstops at first truthy - Membership testing:
inon dictionaries checks keys, not values - Integer caching:
ismay work for small integers but shouldn't be relied upon - Bitwise operators: Essential for flags, permissions, and low-level operations
- Use parentheses: When operator precedence is unclear, always use parentheses for clarity
Next: Python Strings — Learn about string manipulation, formatting, and methods.