Module 1: Object model and reference semantics
Names bind to objects: why = is not memcpy
After this lesson you can predict, without running the code, whether an object changes through a second name bound to the same list or dict.
In C a variable is a named region of memory with a fixed size and type, allocated by the
compiler on the stack or explicitly on the heap. int x = 5; means:
reserve 4 (or 8) bytesreserve a few bytes,
put the bit pattern of 5 there, and every later x = … is a memcpy-like write
of new bytes into the same cell. int y = x; allocates a new cell and
copies the contents of x into it: from then on x and
y are two independent stores.
In Python a name is not a memory cell. A name is a binding, an entry in a namespace that
refers to an object living on the interpreter's heap. x = 5 does not reserve
a cell called x; it creates (or finds) the object 5 and points the name at
it. The next x = "hello" doesn't overwrite x with another type;
it moves the binding to a different object. The name never had a size or a type. The
object has the type.
id() returns the object's address in memoryThe
id() function returns an identifier for an object (in CPython it is related to
the object's address in memory) and lets you check whether two names refer to the
same object.
| What you know from C | In Python | What changes |
|---|---|---|
int y = x; copies bytes into a new cell |
b = a binds a second name to the same object |
Nothing is copied. Two names, one object. |
| Assignment writes into the variable | Assignment re-points the name | The name has no type or size. The object does. |
| A struct copy is independent | A list reached through two names is one list | A mutation through b is visible through a. |
The lesson's first figure, redrawn as a table for this excerpt.
a = [1, 2, 3]
b = a # b and a are two names for the same list
b.append(4)
print(a) # [1, 2, 3, 4]
C reasoning says: “b = a is a struct copy, so a is untouched.”
That is wrong. [1, 2, 3] is an object on the heap; a and
b are two names bound to it. append mutates the object in place,
so the change is visible through both names.
Take away
= in Python binds a name to an object; it does not copy bytes. Several names
for one object is the normal state of a program, not a side effect. Whether the other
names “see” a change comes down to two questions: is the object mutable, and does the
operation mutate it in place or create a new one?
This lesson's check
- 5 claims kept. Each was found in the knowledge base built for this course.
- 2 softened. The sources were silent, so the sentence lost its false precision. Never certified.
- 2 removals. Unreproducible values in a figure, and two duplicate diagrams.
- Second opinion. A different model re-checked and could not corroborate two of the claims from the sources. It can only lower trust, never raise it, and the lesson says so.
Checked is not proven. Two models agreeing makes a claim better supported, not true. Where a check doesn't finish, the lesson is labelled amber. It is never painted green.
Deliberately left out of this course
- asyncio and async/await. A separate model of thinking; not needed for a first move to idiomatic synchronous Python.
- Deep OOP: metaclasses, multiple inheritance, descriptors. A C programmer doesn't write classes by C inertia; the risk lives in the object model and exceptions.
- C extensions, ctypes, embedding CPython. Useful for optimisation, but the step after idiomatic Python, not part of the move.
- Domain libraries: numpy, pandas, web frameworks. Not the language itself; built on top of the idioms this course teaches.
- Profiling, Cython, PyPy. Needs confident idiomatic code first; in two weeks it is excess depth.