Ralfiz Academy
Free lesson · Django 1.1

Python traps every junior ships once

Mutable default arguments, shallow versus deep copies, and the comprehension and collections tools you will use every day to summarise business data.

About 60 minutesIntern bootcampDay 1 · Python core

At a glance

3 min read

Mutable default arguments, shallow versus deep copies, and the comprehension and collections tools you will use every day to summarise business data.

In plain words

Python names point at objects. When two names point at the same list or dict, changing it through one name changes what the other name sees. Most “my cart has items from another customer” bugs come from this one fact.

Trap 1: the mutable default argument

A default value is created once, when the function is defined, not every time it is called. A list default is therefore shared by every call.

def add_item_buggy(item, cart=[]):        # default list is created ONCE
    cart.append(item)
    return cart

def add_item(item, cart=None):            # correct pattern
    if cart is None:
        cart = []
    cart.append(item)
    return cart

print(add_item_buggy("watch"), add_item_buggy("strap"))   # ['watch', 'strap'] twice!
print(add_item("watch"), add_item("strap"))               # ['watch'] ['strap']

Rule: never use a list, dict or set as a default. Use None and create the object inside the function.

Trap 2: shallow copy

copy.copy() copies only the outer container. The inner list of items is still shared. copy.deepcopy() copies all the way down.

order = {"id": 1, "items": [{"sku": "W1", "qty": 1}]}
shallow, deep = copy.copy(order), copy.deepcopy(order)
order["items"][0]["qty"] = 5
print(shallow["items"][0]["qty"], deep["items"][0]["qty"])  # 5 1

Identity, equality and fast lookups

  • == compares values; is compares identity (the same object). Use is only for None, True and False.
  • Immutable: int, float, str, tuple, frozenset, Decimal. Mutable: list, dict, set and most objects.
  • Membership in a set is O(1); in a list it is O(n). Keep “already processed IDs” in a set.
  • sorted(data, key=...) returns a new list; list.sort() sorts in place and returns None.

Summarising data in one line

Business code is mostly “group these rows and add them up”. Learn these tools well:

ToolUse it for
sum(x for x in rows if ...)A total with a filter, without building a list
defaultdict(list)Group rows by a key: order ids per customer
Counter(...)Count by a key; .most_common(n) gives the top n
sorted(rows, key=..., reverse=True)[:n]Top n rows by a field
{k: v for ...} and {x for ...}Dict and set comprehensions: lookup tables and unique values
any() / all()“Is there at least one refunded order?” / “Are all amounts positive?”
paid_total = sum(o["amount"] for o in orders if o["status"] == "paid")
ids_by_customer = defaultdict(list)
for o in orders:
    ids_by_customer[o["customer"]].append(o["id"])
status_count = Counter(o["status"] for o in orders)
top_two = sorted(orders, key=lambda o: o["amount"], reverse=True)[:2]

Unpacking keeps loops readable: first, *rest = ids, and for i, (name, amount) in enumerate(zip(names, amounts), start=1).

Readability check: use a comprehension when it fits on one or two lines and reads like English. If it has two for clauses and an if, write a loop. Your teammate reads code ten times more often than you write it.

The daily workflow

Every Ralfiz project has its own virtual environment and a pinned requirements file. “Works on my machine” is not an acceptable status.

python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
git switch -c feat/sales-report
git add -p && git commit -m "feat: validate sales rows"
Never commit .env files, passwords, API keys, .venv/ or database files. A key leaked in a public repo is abused within minutes.

Key terms

Mutable
An object that can change in place: list, dict, set. Strings, ints and tuples are immutable.
Mutable default argument
A list or dict used as a parameter default. It is created once and shared by every call.
Shallow copy
copy.copy(): a new outer object whose inner objects are still shared with the original.
Deep copy
copy.deepcopy(): copies the object and everything inside it.
Generator expression
A comprehension in round brackets, e.g. sum(x for x in rows). It yields values one by one without building a list.
defaultdict
A dict that creates a default value (an empty list, 0) the first time a missing key is used.
Counter
A dict subclass that counts things; most_common(n) returns the n most frequent.
TipIn interviews, “what does this print?” with a list default argument is one of the most common Python questions. Explain that defaults are evaluated once at definition time.
Hands-on lab6 steps · 1 exercise file
Quiz5 exam-style questions with explanations
Practice tests30 and 50-question mocks, scored out of 1000

The lab, quiz and practice tests for this lesson are for enrolled students, with progress saved to your own login.