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;iscompares identity (the same object). Useisonly forNone,TrueandFalse.- Immutable:
int,float,str,tuple,frozenset,Decimal. Mutable:list,dict,setand most objects. - Membership in a
setis O(1); in alistit is O(n). Keep “already processed IDs” in a set. sorted(data, key=...)returns a new list;list.sort()sorts in place and returnsNone.
Summarising data in one line
Business code is mostly “group these rows and add them up”. Learn these tools well:
| Tool | Use 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).
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"
.env files, passwords, API keys, .venv/ or database files. A key leaked in a public repo is abused within minutes.