Fundamentals 12 min read

Common Python Pitfalls and How to Avoid Them

This article surveys typical Python traps—including UnboundLocalError, mutable default arguments, in‑place versus out‑of‑place updates, tuple syntax, shared mutable containers, list mutation during iteration, closure late binding, __del__ pitfalls, import inconsistencies, version differences, operator quirks, attribute magic methods, and the GIL—explaining why they occur and offering safe alternatives.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Common Python Pitfalls and How to Avoid Them

The author defines a "trap" as code that appears to work but behaves unexpectedly, distinguishing it from outright errors that raise exceptions.

1 Mutable objects as default arguments

Using a mutable object (e.g., a list) as a default parameter causes the same object to be shared across calls, leading to surprising accumulation of state.

<code>>> def f(lst = []):
...     lst.append(1)
...     return lst
>>> f()
[1]
>>> f()
[1, 1]</code>

The fix is to use None as the default and create a new object inside the function.

<code>>> def report(when=None):
...     if when is None:
...         when = time.time()
...     return when
>>> report()
1500113446.746997
>>> report()
1500113448.552873</code>

2 x += y vs x = x + y

While both forms often yield the same result, += modifies the object in place (preserving its identity), whereas x = x + y creates a new object.

<code>>> x=[1]; print id(x); x=x+[2]; print id(x)
4357132800
4357132728
>>> x=[1]; print id(x); x+=[2]; print id(x)
4357132800
4357132800</code>

3 Parentheses – creating tuples

Parentheses denote a tuple, which is immutable. A single element requires a trailing comma to be recognized as a tuple.

<code>>> a = (1,)
>>> type(a)
<class 'tuple'>
>>> a = (1)
>>> type(a)
<class 'int'>
</code>

4 Generating a list of lists

Using the multiplication operator on a mutable list creates multiple references to the same inner list, causing unintended shared mutations.

<code>>> a = [[]] * 10
>>> a[0].append(10)
>>> a
[[10], [10], [10], [10], [10], [10], [10], [10], [10], [10]]</code>

The correct approach is a list comprehension:

<code>>> a = [[] for _ in range(10)]
>>> a[0].append(10)
>>> a
[[10], [], [], [], [], [], [], [], [], []]
</code>

5 Modifying a list while iterating

Deleting elements from a list during iteration can skip items because the index continues to increase while the list shrinks.

<code>>> def modify_lst(lst):
...     for idx, elem in enumerate(lst):
...         if elem % 3 == 0:
...             del lst[idx]
>>> lst = [1,2,3,4,5,6]
>>> modify_lst(lst)
>>> lst
[1, 2, 4, 5]
</code>

A safer alternative is to build a new list with a comprehension.

6 Closures and lambda late binding

Lambda functions inside a loop capture the loop variable by reference, so all generated functions see the final value of the variable.

<code>>> def create_multipliers():
...     return [lambda x: i*x for i in range(5)]
>>> for m in create_multipliers():
...     print m(2)
8
8
8
8
8
</code>

Fix by binding the current value as a default argument:

<code>>> def create_multipliers():
...     return [lambda x, i=i: i*x for i in range(5)]
</code>

7 Defining __del__

Objects with a __del__ method can break Python's cyclic‑garbage collector, potentially causing memory leaks.

8 Different import styles for the same module

Importing a module inside functions using different syntaxes can create distinct module objects (different id values), leading to state inconsistencies.

9 Python version upgrades

Python 3 changes the return types of range , map , filter , and dict.items from lists to iterators, which can affect code that expects a list.

10 ++i — i

In Python, the expression ++i - i leaves i unchanged because the double plus is interpreted as two unary plus operators.

11 setattr, getattr, __getattribute__

These magic methods control attribute access; getattr is only invoked when normal lookup fails, while __setattr__ and __getattribute__ handle all attribute assignments and retrievals.

12 GIL

The Global Interpreter Lock is a well‑known limitation of CPython that prevents true parallel execution of Python bytecode in multiple native threads.

Conclusion

Python is easy to learn and powerful, but being aware of these common pitfalls helps developers write more reliable and maintainable code.

GILClosuresImportpitfallsattribute-accessmutable-default-args
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

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.