Python Essentials for AI Engineer

Course Content

Python Essentials for AI Engineer

6 sections · 48 lessons

What is Mutability?


Copying an LLM request templatereq = base.copy()• New outer dict• messages list is still shared• append leaks into base• Customer B sees customer A's questionreq = copy.deepcopy(base)• New outer dict• New messages list as well• append touches only req• base keeps one system message
A shallow copy duplicates the container, not what is inside it — nested lists stay shared between every request.

What you need to know

Variables are labels, not boxes

a = [1, 2] creates one list object and attaches the label a to it. b = a attaches a second label to the same object; it copies nothing.

Python
a = [1, 2]b = ab.append(3)print(a)              # [1, 2, 3]print(a is b)         # True -> one object, two names

Immutable objects are replaced, not changed

Python
s = "hello"old_id = id(s)s += " world"                 # builds a NEW stringprint(id(s) == old_id)        # False

id() returns an object's identity. The string "hello" was never modified; the name s now points to a new string. The same happens with x += 1 on an int.

Functions receive references

Python passes arguments by object reference (often called "pass by assignment"). The function gets a name that points at the caller's object. If the object is mutable and the function mutates it, the caller sees the change. If the function rebinds the name (items = []), the caller is unaffected.

Python
def add_tag(tags):    tags.append("reviewed")   # mutates the caller's listmy_tags = ["upi"]add_tag(my_tags)print(my_tags)                # ['upi', 'reviewed']

Shallow vs deep copy

  • b = a — no copy, same object.
  • a.copy(), list(a), a[:], copy.copy(a) — shallow copy: a new outer container, but the inner objects are shared.
  • copy.deepcopy(a) — deep copy: everything is copied recursively.

A real-life example

You keep a base LLM request and build one request per customer from it. A shallow copy looks correct but leaks messages between customers:

Python
import copybase = {"model": "chat-small", "messages": [{"role": "system", "content": "You are a support bot."}]}req1 = base.copy()                                   # shallowreq1["messages"].append({"role": "user", "content": "Where is my refund?"})print(len(base["messages"]))                         # 2  -> base was changed!base["messages"].pop()                               # undo the damagereq2 = copy.deepcopy(base)req2["messages"].append({"role": "user", "content": "Cancel my order."})print(len(base["messages"]))                         # 1  -> base untouched

In production this bug shows up as customer B's prompt containing customer A's question — a privacy incident, not just a wrong answer. Deep-copy the template, or better, build a fresh dict for each request.

Follow-up questions to expect

  • "Is Python pass-by-value or pass-by-reference?" — Neither exactly. It passes object references by value: the function can mutate a mutable argument but cannot rebind the caller's variable.
  • "Why must dict keys be immutable?" — The dict stores a key by its hash. If the key changed, its hash would change and the dict could never find it again.
  • "Is a string mutable?" — No. Every string method, like .replace() or .upper(), returns a new string.