Python Mastery: Complete Beginner to Professional
HomeInsightsCoursesPythonAssignment & Comparison Operators
Python Fundamentals

Assignment & Comparison Operators

Master the foundational operators that power every Python program - from simple variable assignments to complex logical comparisons that drive decision-making in your code.

Operators are the building blocks of any programming language, and Python's operator system is both elegant and powerful. In this chapter, we'll explore two critical categories: assignment operators, which store and update values in memory, and comparison operators, which evaluate relationships between values to produce boolean results. Understanding these operators isn't just about memorizing syntax—it's about developing an intuition for how Python manipulates data at a fundamental level.

Whether you're building a simple calculator, processing user input, or implementing complex business logic, you'll use these operators constantly. By the end of this chapter, you'll understand not just the "what" and "how," but also the "why" behind Python's operator design choices, including common pitfalls that trip up even experienced developers.

What You'll Learn

  • How assignment operators work with Python's reference model
  • All compound assignment operators and their performance implications
  • Comparison operators and the critical difference between == and is
  • Common mistakes that cause bugs in production code
  • Best practices for writing clean, readable comparisons

Assignment Operators: Storing Values in Memory

The assignment operator (=) is your primary tool for storing and updating values in Python. Unlike mathematical equality, assignment is a directive: "Take the value on the right and bind it to the name on the left." This distinction is crucial because Python variables are references to objects, not containers that hold values directly.

Basic Assignment
PYTHON
# Simple assignment
x = 10          # x now references the integer object 10
name = "Alice"  # name references the string object "Alice"
is_valid = True # is_valid references the boolean True

# Multiple assignment
a, b, c = 1, 2, 3  # Assigns 1 to a, 2 to b, 3 to c
x = y = z = 0      # All three reference the same integer 0

When you write x = 10, Python creates (or reuses) an integer object with value 10 in memory, then creates a binding in the current namespace from the name "x" to that object. This reference model has important implications for how variables behave, especially with mutable objects like lists and dictionaries.

💡
Pro Tip: Assignment is not a copy operation. When you assign one variable to another (b = a), both variables reference the same object in memory. This matters significantly with mutable objects.

Advanced Assignment: Unpacking Power

One of Python's most beloved features is "Unpacking" (also known as Destructuring). It allows you to assign multiple variables from a single iterable in one clean line.

1. Tuple Unpacking & Swapping

Why write three lines to swap variables when you can write one?

PYTHON
# The Old Way (C/Java style)
temp = a
a = b
b = temp

# The Python Way
a, b = b, a  # Swaps values instanty!

# Unpacking a tuple
coordinates = (10, 20)
x, y = coordinates  # x=10, y=20

2. Extended Iterable Unpacking (*)

Introduced in Python 3, the asterisk (*) operator allows you to grab "everything else" into a list. This is incredibly useful when parsing data streams or CSV rows.

PYTHON
# Grab the first and last, ignore the middle
scores = [10, 50, 60, 90, 100]
first, *middle, last = scores

print(first)   # 10
print(middle)  # [50, 60, 90] (Always a list!)
print(last)    # 100

# Head/Tail separation (LISP style)
head, *tail = [1, 2, 3, 4]
print(head) # 1
print(tail) # [2, 3, 4]

3. Deep Unpacking & Ignored Values

You can unpack nested structures and use _ (underscore) for values you don't care about.

PYTHON
user_record = ("Alice", 25, ("NY", "USA"))

# Unpack name and country only
name, _, (_, country) = user_record

print(name)     # "Alice"
print(country)  # "USA"
# _ is assigned 25, but we signal "ignore this"
💡
Performance Note: Unpacking is not just syntactic sugar; it compiles to specific bytecode instructions (UNPACK_SEQUENCE) which are often faster than manual indexing (x = data[0]).

Compound Assignment Operators

Compound assignment operators combine an operation with assignment, providing a concise way to update variables. These operators are not just syntactic sugar—they can be more efficient and express intent more clearly than their expanded forms.

All Compound Assignment Operators
PYTHON
# Arithmetic compound assignments
count = 10
count += 5   # count = count + 5  → 15
count -= 3   # count = count - 3  → 12
count *= 2   # count = count * 2  → 24
count /= 4   # count = count / 4  → 6.0 (float division)
count //= 2  # count = count // 2 → 3.0 (floor division)
count %= 2   # count = count % 2  → 1.0 (modulo)
count **= 3  # count = count ** 3 → 1.0 (exponentiation)

# String concatenation
message = "Hello"
message += " World"  # "Hello World"

# List extension
numbers = [1, 2, 3]
numbers += [4, 5]    # [1, 2, 3, 4, 5]

Each compound operator modifies the variable in place when possible. For immutable types like integers and strings, a new object is created and the variable is rebound. For mutable types like lists, the object itself is modified, which can have surprising effects when multiple variables reference the same list.

Compound Assignment Operators Reference

OperatorExampleEquivalent ToDescription
+=x += 3x = x + 3Add and assign
-=x -= 3x = x - 3Subtract and assign
*=x *= 3x = x * 3Multiply and assign
/=x /= 3x = x / 3Divide and assign (float)
//=x //= 3x = x // 3Floor divide and assign
%=x %= 3x = x % 3Modulo and assign
**=x **= 3x = x ** 3Exponentiate and assign

The Walrus Operator (:=)

Introduced in Python 3.8, the Assignment Expression operator (:=) allows you to assign a value to a variable inside an expression. It is a powerful tool for condensing loops and comprehensions.

Because this operator fundamentally changes how Python expressions work, we have dedicated an entire lesson to it. Learn how to use it to clean up while loops and optimize list comprehensions in the dedicated module.

Bitwise Compound Assignment

While arithmetic assignments are common, bitwise assignments (`|=`, `&=`) are secret weapons for managing state flags efficiently.

PYTHON
# Permissions System
READ = 1   # 001
WRITE = 2  # 010
EXEC = 4   # 100

user_perms = 0

# Grant permissions (Bitwise OR)
user_perms |= READ   # 001
user_perms |= WRITE  # 011

# Revoke permissions (Bitwise AND with Inverse)
user_perms &= ~WRITE # 001

# Check permission
has_read = (user_perms & READ) == READ

Comparison Operators: Evaluating Relationships

Comparison operators evaluate the relationship between two values and return a boolean result (True or False). These operators are the foundation of conditional logic, enabling your programs to make decisions based on data. Python supports six comparison operators, each serving a specific purpose in logical expressions.

Comparison Operators in Action
PYTHON
x = 10
y = 5

# Equality and inequality
print(x == y)   # False - Equal to?
print(x != y)   # True  - Not equal to?

# Relational comparisons
print(x > y)    # True  - Greater than?
print(x < y)    # False - Less than?
print(x >= 10)  # True  - Greater than or equal to?
print(x <= 5)   # False - Less than or equal to?

# Chained comparisons (Pythonic!)
age = 25
print(18 <= age < 65)  # True - age is between 18 and 65

Python's comparison operators work intuitively with numbers, but they also support comparisons between strings (lexicographic order), lists (element-by-element), and even custom objects if you implement the appropriate magic methods. The ability to chain comparisons (a < b < c) is a Pythonic feature that makes range checks incredibly readable.

Comparing Different Data Types
PYTHON
# String comparison (lexicographic order)
print("apple" < "banana")   # True
print("Python" == "python") # False (case-sensitive)

# List comparison (element by element)
print([1, 2, 3] < [1, 2, 4])  # True
print([1, 2] < [1, 2, 0])     # False

# Tuple comparison
print((1, 2) < (1, 3))  # True
print((2, 1) < (1, 3))  # False

The Critical Difference: == vs is

One of the most important distinctions in Python is between the == operator and the is operator. While == checks for value equality (are these two things equal?), is checks for identity (are these the exact same object in memory?). Confusing these two is a common source of subtle bugs.

Equality vs Identity
PYTHON
# Equality (==) - compares values
list1 = [1, 2, 3]
list2 = [1, 2, 3]
print(list1 == list2)  # True - same values
print(list1 is list2)  # False - different objects

# Identity (is) - compares object IDs
list3 = list1
print(list1 is list3)  # True - exact same object

# The None check (the correct use of 'is')
value = None
if value is None:  # ✅ Correct
    print("Value is None")

if value == None:  # ❌ Works but not recommended
    print("This works but use 'is' instead")
⚠️
Important: Always use is when comparing to None,True, or False. These are singleton objects in Python, and identity checks are both faster and more semantically correct.

Cloning Data: Shallow vs Deep Copy

Since assignment (=) only creates references, how do you actually copy an object? If you want to modify a list without affecting the original, you need to clone it. Python offers two ways to do this, and mixing them up is a common source of bugs.

1. Shallow Copy

Creates a new container, but populates it with references to the same child objects. This is the default behavior of slicing [:] and the list() constructor.

PYTHON
original = [[1, 2], [3, 4]]
shallow = original[:]  # or list(original)

# Modifying the container is safe...
shallow.append([5, 6])
print(original) # [[1, 2], [3, 4]] (Unaffected)

# BUT modifying a child affects BOTH!
shallow[0][0] = 999
print(original) # [[999, 2], [3, 4]] (Affected!)
# Why? Because shallow[0] and original[0] point to the SAME list object.

2. Deep Copy

Recursively copies everything. It creates a new container AND new child objects (and new children of children...). This is slower but truly independent.

PYTHON
import copy

original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)

deep[0][0] = 999
print(original) # [[1, 2], [3, 4]] (Clean!)

The Scope of Assignment: global and nonlocal

By default, when you assign to a variable inside a function (x = 10), Python creates a local variable, even if a global variable with the same name exists. This protects your global state from accidental modification. But what if you want to modify the outer state?

1. The `global` Keyword

Use `global` to tell Python: "Don't create a new local variable; use the one defined at the top level."

PYTHON
counter = 0

def increment():
    # counter += 1  # ❌ UnboundLocalError! Python sees assignment and assumes local.
    
    global counter
    counter += 1    # ✅ Modifies the global variable
    print(counter)

2. The `nonlocal` Keyword

Used in nested functions (closures). It tells Python to look in the nearest enclosing scope that isn't global.

PYTHON
def outer():
    count = 0
    
    def inner():
        nonlocal count
        count += 1
        return count
        
    return inner

# This creates a stateful closure!
fn = outer()
print(fn()) # 1
print(fn()) # 2
⚠️
Architecture Warning: heavily relying on global is often a sign of poor design. It makes code hard to test and debug because functions have "hidden inputs" (the global state). Prefer passing arguments and returning values.

Real-World Applications

1. Input Validation Logic

One of the most common uses for comparison operators is validating user input. Whether you are building a CLI tool or a Web API, you must ensure data falls within safe boundaries.

PYTHON
def validate_age(age):
    """Validate age is within acceptable range"""
    # Chained comparison simplifies logic
    if not (0 <= age <= 150):
        raise ValueError("Age must be between 0 and 150")
    
    # Categorization logic
    if age < 18:
        return "minor"
    elif age >= 65:
        return "senior"
    else:
        return "adult"

def validate_password(password):
    """Check constraints using short-circuit logic"""
    # Fail fast if length is wrong
    if len(password) < 8:
        return False
    
    # Check composition using any()
    # These return Booleans directly - no need for 'if'
    has_digit = any(c.isdigit() for c in password)
    has_upper = any(c.isupper() for c in password)
    
    return has_digit and has_upper

2. The Accumulator Pattern

Compound assignment operators (+=) are the backbone of "Accumulator" classes, used for statistics, scoring, or logging. They are cleaner than x = x + 1.

PYTHON
class WebsiteAnalytics:
    def __init__(self):
        self.page_views = 0
        self.total_time = 0.0
    
    def record_visit(self, duration):
        # Update multiple metrics in place
        self.page_views += 1
        self.total_time += duration
    
    def average_time(self):
        # Compare vs 0 to avoid ZeroDivisionError
        if self.page_views == 0:
            return 0
        return self.total_time / self.page_views

3. Filtering & Comprehensions

Comparison operators define the logic for filtering datasets. When used inside List Comprehensions, they create a declarative way to query data.

PYTHON
products = [
    {'{'}"name": "Laptop", "price": 999, "rating": 4.5{'}'},
    {'{'}"name": "Mouse", "price": 25, "rating": 4.2{'}'},
    {'{'}"name": "Keyboard", "price": 75, "rating": 4.7{'}'}
]

# "Select * from products where price between 20 and 100"
affordable = [
    p for p in products 
    if 20 <= p["price"] <= 100
]

# "Select * from products order by price"
# Lambda uses the comparison value
sorted_products = sorted(products, key=lambda p: p["price"])

Common Pitfalls & How to Avoid Them

❌ Mistake 1: Using = Instead of == in Conditions

Why it's wrong: Assignment (=) instead of comparison (==) in an if statement will cause a SyntaxErrorin Python (unlike C/JavaScript where it silently assigns).

PYTHON
# ❌ WRONG - SyntaxError
if x = 10:
    print("This won't work")

# ✅ CORRECT
if x == 10:
    print("This works!")

How to avoid: Python actually protects you from this error! The syntax error is intentional. In other languages, this is a dangerous bug, but Python's design prevents it.

❌ Mistake 2: Comparing Floats with ==

Why it's wrong: Floating-point arithmetic is imprecise due to binary representation. Direct equality checks can fail unexpectably.

PYTHON
# ❌ WRONG - Unreliable
result = 0.1 + 0.2
if result == 0.3:  # Might be False!
    print("Equal")

print(0.1 + 0.2)  # 0.30000000000000004

# ✅ CORRECT - Use tolerance
import math

def float_equal(a, b, tolerance=1e-9):
    return abs(a - b) < tolerance

if float_equal(0.1 + 0.2, 0.3):
    print("Equal within tolerance")

# Or use the math module
if math.isclose(0.1 + 0.2, 0.3):
    print("Close enough!")

❌ Mistake 3: Using is for Value Comparison

Why it's wrong: is checks object identity, not value equality. Due to Python's integer caching, it might work for small numbers but fail for larger ones.

PYTHON
# ❌ WRONG - Works sometimes, fails others
a = 1000
b = 1000
if a is b:  # False (different objects)
    print("Same")

# This confusingly works due to integer caching
x = 5
y = 5
if x is y:  # True (cached integers -5 to 256)
    print("Same")

# ✅ CORRECT - Use == for value comparison
if a == b:
    print("Equal values")

# ✅ Only use 'is' for None, True, False
if value is None:
    print("Value is None")

❌ Mistake 4: Unexpected Behavior with += on Lists

Why it's wrong: += modifies lists in-place, affecting all references to that list object.

PYTHON
# ❌ UNEXPECTED BEHAVIOR
original = [1, 2, 3]
alias = original
alias += [4, 5]

print(original)  # [1, 2, 3, 4, 5] - Modified!
print(alias)     # [1, 2, 3, 4, 5]

# ✅ CORRECT - Create a new list if you don't want to modify
original = [1, 2, 3]
new_list = original + [4, 5]  # Creates new list

print(original)  # [1, 2, 3] - Unchanged
print(new_list)  # [1, 2, 3, 4, 5]

❌ Mistake 5: The Mutable Default Argument

Why it's wrong: Default arguments are evaluated only once when the function is defined, not every time it is called. If you use a mutable object (like a list) as a default, it persists across calls.

PYTHON
# ❌ WRONG
def add_item(item, data=[]):
    data.append(item)
    return data

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2] 😱 - The list grew!

# ✅ CORRECT - Use None as a sentinel
def add_item_safe(item, data=None):
    if data is None:
        data = []  # Create a NEW list every time
    data.append(item)
    return data

Best Practices

✅ Do:

  • Use compound assignment operators (+=, -=) for clarity and efficiency
  • Always use is when comparing to None
  • Chain comparison operators for range checks: 0 <= x < 100
  • Use math.isclose() for floating-point comparisons
  • Be explicit with parentheses in complex comparisons
  • Use != instead of not == for clarity

❌ Don't:

  • Don't use == True or == False - use the value directly
  • Don't use is for value comparisons (except None)
  • Don't compare floats with == without tolerance
  • Don't chain too many assignments (a = b = c = [] with mutable objects)
  • Don't forget operator precedence in complex expressions
  • Don't use assignment in conditions (Python prevents this anyway)

🎯 Key Takeaways

1. Assignment Binds Names to Objects

The = operator creates a reference from a variable name to an object in memory, not a copy. This matters especially with mutable objects.

2. Compound Operators Are Efficient

Operators like += and *= modify variables in-place when possible, making code more concise and often more performant.

3. == Checks Value, is Checks Identity

Use == to compare values (equality) and is to check if two variables reference the same object (identity). Always use is None.

4. Comparison Operators Return Booleans

All comparison operators (<, >, ==, etc.) return True or False, forming the foundation of conditional logic.

5. Chain Comparisons for Readability

Python allows chaining: a < b < c is more readable and efficient thana < b and b < c.

6. Beware of Floating-Point Comparison

Never use == for floats directly. Use math.isclose() with appropriate tolerance instead.

What's Next?

Now that you've mastered assignment and comparison operators, you're ready to explore arithmetic operators and operator precedence - essential for mathematical calculations and complex expressions in Python.