Fundamentals 10 min read

10 Common Python Anti‑Patterns and How to Replace Them

Discover the most subtle Python anti‑patterns—from using is for value comparison to overusing map and filter—explaining why they harm readability, performance, or correctness, and learn clear, idiomatic alternatives that make your code cleaner, faster, and more maintainable.

Code Mala Tang
Code Mala Tang
Code Mala Tang
10 Common Python Anti‑Patterns and How to Replace Them

If you frequently write Python code, you may unknowingly use some patterns that do more harm than good. Below are common Python anti‑patterns (often subtle, hidden, and easy to overlook) and better alternatives to make your code cleaner, more efficient, and easier to read.

1. Using is for string or integer comparison

<code>a = "hello"
if a is "hello":
    print("Match")</code>

Why this is bad:

is checks object identity, not value equality.

It may work due to Python's string interning, but can fail unpredictably across versions or inputs.

Better alternative:

<code>if a == "hello":
    print("Match")</code>

Reason: == checks value equality and always works as expected, avoiding platform or version differences.

2. Misusing try/except as control flow

<code>try:
    my_dict[key]
except KeyError:
    my_dict[key] = "default"
</code>

Why this is bad:

Catching exceptions is more expensive and slower than proper handling.

It makes code harder to read—you are catching exceptions instead of preventing them.

Better alternative:

<code>my_dict.setdefault(key, "default")
</code>

or

<code>if key not in my_dict:
    my_dict[key] = "default"
</code>

Reason: More efficient and idiomatic Python.

3. Using lambda for complex logic

<code>my_func = lambda x: x * 2 if x > 10 else (x + 5 if x > 5 else x - 1)
</code>

Why this is bad:

Very hard to read.

Violates the principle that lambda should be short and simple.

Better alternative:

<code>def my_func(x):
    if x > 10:
        return x * 2
    elif x > 5:
        return x + 5
    else:
        return x - 1
</code>

Reason: Readability > cleverness; easier to debug, test, and maintain.

4. Overusing list comprehensions (especially nested)

<code>matrix = [[i * j for j in range(5)] for i in range(5)]
</code>

Why this is bad:

Readability drops quickly with deep nesting.

Hard to debug or maintain.

Better alternative:

<code>matrix = []
for i in range(5):
    row = []
    for j in range(5):
        row.append(i * j)
    matrix.append(row)
</code>

Reason: Easier to read and to add logging, debugging, or conditional logic.

5. Misusing @property for expensive calculations

<code>class Data:
    @property
    def processed(self):
        # time‑consuming processing
        time.sleep(5)
        return "done"
</code>

Why this is bad:

Appears lightweight but may perform expensive computation or have side effects.

Confuses users, violating the expected semantics of a property.

Better alternative:

<code>class Data:
    def get_processed(self):
        time.sleep(5)
        return "done"
</code>

or, if caching is needed:

<code>from functools import cached_property

class Data:
    @cached_property
    def processed(self):
        time.sleep(5)
        return "done"
</code>

Reason: Clearer intent; cached_property is lazy and runs only once, ideal for expensive calculations.

6. Unnecessarily using *args and **kwargs

<code>def greet(*args, **kwargs):
    print("Hello", kwargs.get('name', 'stranger'))
</code>

Why this is bad:

Too flexible; API becomes harder to understand, autocomplete fails, and type hints cannot be used.

Unnecessarily hides the function signature.

Better alternative:

<code>def greet(name="stranger"):
    print("Hello", name)
</code>

Reason: Explicit > implicit; IDEs and linters are happier, and future you will thank yourself.

7. Using list comprehensions for side effects

<code>[print(x) for x in range(10)]
</code>

Why this is bad:

It returns a list of None values.

Misuses a construct meant for generating lists, not performing actions.

Better alternative:

<code>for x in range(10):
    print(x)
</code>

Reason: Keeps code clear, Pythonic, and avoids creating useless garbage data.

8. Using dict() and keyword arguments for dynamic keys

<code>keys = ["name", "age"]
values = ["Skilled", 30]
my_dict = dict(name="Skilled", age=30)  # fails in dynamic contexts!
</code>

Why this is bad:

Only works for hard‑coded keys.

Fails silently in dynamic contexts.

Better alternative:

<code>my_dict = dict(zip(keys, values))
</code>

or more concisely:

<code>my_dict = {k: v for k, v in zip(keys, values)}
</code>

Reason: Works for both static and dynamic scenarios; more flexible and intuitive.

9. Using slicing [:] to copy lists instead of copy() or deepcopy()

<code>copy_list = original_list[:]
</code>

Why this is bad:

Only performs a shallow copy.

Fails silently when the list contains mutable elements (e.g., nested lists).

Better alternative:

<code>import copy
deep_copy = copy.deepcopy(original_list)
</code>

or for a readable shallow copy:

<code>copy_list = original_list.copy()
</code>

Reason: Clearly expresses the intent to copy and is safer for nested structures.

10. Overcomplicating with map() or filter() when a simple loop suffices

<code>names = ["Skilled", "Coder", "John"]
uppercase = list(map(str.upper, filter(lambda x: len(x) > 4, names)))
</code>

Why this is bad:

Harder to read and less Pythonic.

More difficult to debug or modify.

Better alternative:

<code>uppercase = [name.upper() for name in names if len(name) > 4]
</code>

Reason: List comprehensions are more readable, expressive, and keep the code concise.

Avoiding these patterns not only improves your code quality but also makes future debugging much easier. Stick to the cleaner alternatives and write truly Pythonic code.

PerformancePythonBest PracticesCode readabilityanti-patterns
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.