Fundamentals 8 min read

Master the 6 Most Common Python Pitfalls and How to Avoid Them

This article explores six frequently misunderstood Python features—mutable default arguments, the difference between is and ==, *args/**kwargs, iterator exhaustion, complex list comprehensions, and the global keyword—explaining why they trip developers up and offering clear, practical solutions to master each one.

Code Mala Tang
Code Mala Tang
Code Mala Tang
Master the 6 Most Common Python Pitfalls and How to Avoid Them

Even experienced developers can be tripped up by certain Python features. The author shares personal struggles and presents a concise guide to the most commonly misunderstood aspects of Python, why they are tricky, and how to master them.

1. Mutable Default Arguments

Problem: Functions with default list or dictionary parameters retain values between calls, leading to unexpected behavior.

<code>def add_item(item, items=[]):
    items.append(item)
    return items

print(add_item("apple"))   # ['apple']
print(add_item("banana"))  # ['apple', 'banana']
</code>
Why does apple remain? Shouldn't items be reset to [] ?

Why it’s confusing: Default values are created only once at function definition, not each call.

How to master it: Use None as the default and initialize the list inside the function.

<code>def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

print(add_item("apple"))   # ['apple']
print(add_item("banana"))  # ['banana']
</code>

2. The is vs == Dilemma

Problem: Developers often think is and == are interchangeable, but they are not.

<code>a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)  # True
print(a is b)  # False
</code>

Why it’s confusing: == checks value equality, while is checks object identity.

How to master it: Use is only for identity comparisons (e.g., with None or singletons) and use == for value comparisons.

<code>if a is None:
    print("This checks identity!")
if a == b:
    print("This checks value equality!")
</code>

3. The Mystery of *args and **kwargs

Problem: The asterisks in function definitions look like Python hieroglyphs; their meaning is unclear.

<code>def func(*args, **kwargs):
    print(args)
    print(kwargs)

func(1, 2, 3, name="Alice", age=25)
# args -> (1, 2, 3)
# kwargs -> {'name': 'Alice', 'age': 25}
</code>

Why it’s confusing: Beginners may not know how to use them effectively.

How to master it: *args collects an arbitrary number of positional arguments into a tuple, while **kwargs collects keyword arguments into a dictionary.

*args passes a variable number of positional arguments to a function, packing them into a tuple.

**kwargs passes a variable number of keyword arguments, packing them into a dictionary.

<code>def greet(*args, **kwargs):
    for name in args:
        print(f"Hello, {name}!")
    for key, value in kwargs.items():
        print(f"{key} = {value}")

greet("Alice", "Bob", language="Python", level="Intermediate")
</code>

4. Iterator Characteristics

Problem: Attempting to iterate over the same iterator multiple times.

<code>it = iter([1, 2, 3])
print(list(it))  # [1, 2, 3]
print(list(it))  # []
</code>

How to master it: Iterators are single‑use objects; after they are exhausted they cannot be reused. Use a reusable iterable (like a list or tuple) or create a new iterator when you need to traverse again.

<code>lst = [1, 2, 3]
for x in lst:
    print(x)  # first pass
for x in lst:
    print(x)  # second pass, still works
</code>

Iterators are lazily evaluated; each call to next() produces the next value, and they have no reset mechanism. Once exhausted, a subsequent iteration raises StopIteration .

5. The Quirks of List Comprehensions

Problem: List comprehensions are concise but can quickly become hard to read when they include conditions or nested loops.

<code>nums = [1, 2, 3, 4, 5]
squares = [x ** 2 for x in nums if x % 2 == 0]
</code>

Why it’s confusing: Adding conditions or nesting makes them less readable.

How to master it: Keep them simple; for complex logic, break the computation into separate lines or use a regular loop.

<code># Simple list comprehension
nums = [1, 2, 3, 4, 5]
even_squares = [x ** 2 for x in nums if x % 2 == 0]

# For more complex logic, use a loop
result = []
for x in nums:
    if x % 2 == 0:
        result.append(x ** 2)
</code>

6. The Mystery of the global Keyword

Problem: Many assume variables defined inside a function are global by default, which is not true.

<code>x = 10

def modify():
    x = x + 1  # Error! UnboundLocalError

modify()
</code>

Why it’s confusing: Python assumes you want to create a new local variable unless you explicitly declare otherwise.

How to master it: If you need to modify a global variable, declare it with the global keyword, but avoid using globals when possible.

<code>x = 10

def modify():
    global x
    x = x + 1

modify()
print(x)  # 11
</code>
pythonIteratorsPitfallslist comprehensionidentitydefault argumentsglobalargs kwargs
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.