Assignment is binding, not copying
Lesson 1 said it in passing — names are labels, types live on values. This lesson is where that mental model becomes load-bearing. Once you internalize it, half the bugs in your career evaporate. Misunderstand it, and you'll keep getting bitten by mutability surprises every couple of months.
When you write x = 42, Python does not create a box called x and stuff 42 into it. Python creates an integer object whose value is 42, then makes the name x point at that object. The name and the object are different things — and the same object can have many names pointing at it.
Mutable vs immutable — the divide that runs through the whole language
Every Python value is either mutable (you can change its contents in place) or immutable (you cannot). The divide is fixed by type:
- Immutable — int, float, str, bool, tuple, frozenset, bytes
- Mutable — list, dict, set, bytearray, and every class instance unless explicitly frozen
Why this matters: when two names point at the same mutable object, modifying through one name is visible through the other. There is only one object. When two names point at the same immutable object, you can't modify it at all — any operation that looks like modification (e.g. x += 1 on an int) actually creates a new object and re-points the name.
a = [1, 2, 3]; b = a; b.append(4). Now a is also [1, 2, 3, 4], because a and b were never two lists. They were always one list with two names. To get a separate list, you have to ask: b = a.copy() (or b = list(a), or b = a[:]).
== vs is — equality vs identity
Two operators that look similar and mean entirely different things:
==tests equality — "do these two values compare equal?" Calls__eq__under the hood; classes can override it.istests identity — "are these two names pointing at the literal same object in memory?" Cannot be overridden.
The rule of thumb — use == for value comparison, use is only for comparing against singletons (None, True, False). PEP 8 codifies this: if x is None:, never if x == None:. Same result, but is None is the idiom.
id() — the object identifier
Python's built-in id(obj) returns a unique integer identifying the object during its lifetime. CPython implements it as the memory address. a is b is equivalent to id(a) == id(b).
You'll rarely use id() in production code, but it's invaluable for understanding what's happening when you're learning. "Is this still the same object after my operation?" — print id() before and after.
Multiple assignment and tuple unpacking
Python lets you assign multiple names in a single statement. The right side is evaluated first as a tuple, then unpacked into the names on the left. This enables some of the most idiomatic Python patterns.
The classic — variable swap. In most languages, swapping a and b requires a temporary variable. In Python: a, b = b, a. The right side becomes the tuple (b, a), then unpacked back into a, b. Atomic, no temp needed.
session_id, message_id = await create_pair(), brain, model = pick_brain_for_task(task), x, y, w, h = avatar_box. It reads as English: "name these two things, here are the values." That readability is the Pythonic payoff.
Augmented assignment — and the surprise with mutables
x += 1 looks like a single operator, but its meaning depends on whether the left side is mutable. For immutable types (int, str, tuple), x += 1 is exactly equivalent to x = x + 1 — Python creates a new object and re-points the name.
For mutable types (list, dict, set), x += y calls __iadd__ if defined, which modifies x in place. list += other appends in place; list = list + other creates a new list. They look similar; they're not the same.
The "default mutable argument" trap (preview)
This combines everything above into Python's most famous beginner trap. It's properly covered in the Flow track, but you should see the shape now:
If you write def foo(items=[]): and call it three times without arguments, you'll find all three calls share the same list. Default arguments are evaluated once when the function is defined, not each time it's called. Mutable defaults persist across calls.
The fix: use def foo(items=None): and inside if items is None: items = []. The Flow track explains the why; for now, just recognize the smell.
Bottom line
Names point at objects. Some objects are mutable, some are not. Two names can point at the same mutable object. == compares values; is compares identity. Once these four sentences feel obvious, the rest of Python's behavior stops surprising you.
is None / is not None for null checks, never == None. Reach for tuple unpacking before introducing a throwaway variable. When you copy a list "just to be safe," be honest about whether you actually need a copy or just a fresh name — copies are cheap but not free, and unnecessary copies hide your real intent.
Exercise 했어. quest내용 다 소화를 못하겠네. 우선 좀 진도 나가고 다시 돌아와서 또 봐야할거 같아.
"b = a는 하나의 객체에 이름표를 하나 더 붙인 것이고, b = a.copy()는 아예 새로운 객체를 만들어 독립시켰기 때문에 결과가 다름"