Unit 3.1 · about 4 h
10 / 324Names, identity, mutability, copying and hashability
Almost every silent data bug is shared mutable state. This unit makes aliasing visible before frameworks add more of it.
- explain
- measure
You can now
- Explain name binding, identity and equality using a novel nested-object example, and state when CPython may reuse an immutable object.
- Predict visible mutations for aliases, shallow copies and deep copies of a nested structure.
- State the dictionary and set key contract, including why a tuple holding a list is unhashable and why NaN lookups fail.
Covers
- Name binding, object identity and equality
- Mutability, aliasing and visible state changes
- Shallow and deep copying, hashability and dictionary key contracts
Study, in this order
4 steps
- Shallow and deep copyingStas' Python Cookbook - selected path
Lists: Copying (shallow vs deep), including the
listB = listAwarning - Tuples, sets and dictionariesStas' Python Cookbook - selected path
Tuples, Sets and Dictionaries: construction, mutation,
frozenset, key rules and insertion order - Objects, values and typesPython language reference
Section 3.1: identity, type and value; mutable versus immutable; the
a = 1;b = 1reuse rule - Default equality and hashingPython language reference
object.__eq__andobject.__hash__: default identity equality; defining__eq__without__hash__removes hashing
Focus on
- Assignment binds a name to an object.
istests identity and==tests value; CPython reuses small immutable objects, but the reference says reuse may or may not happen, so never branch on it. - Two names can own one mutable object. A shallow copy shares every nested object; only a deep copy duplicates them. Copying an immutable such as a
tupleorstrmay return the same object. - A key must stay hashable and equal for its lifetime in the container. A
tupleis hashable only when all its items are; NaN never equals itself, so a fresh NaN misses a stored NaN key. - The cookbook says tuples are hashable because they are immutable. That shortcut is wrong for tuples that hold mutables;
hash((1, [2]))raisesTypeError.
Reproduce closed-book
a, c and d after a = [[0]]; b = a; c = copy.copy(a); d = copy.deepcopy(a); b[0].append(1). Then quote the measured copy costs: shallow about 3 us and deep about 1.4 ms per copy of a 2000 by 10 nested list on CPython 3.14.4 default build, 12th Gen Intel Core i9-12900K, timeit.repeat best of 5.Traps
- Comparing values with
is. Constant folding makes257 is 257true inside one compilation unit, and CPython warns (SyntaxWarning) oniswith a literal, soisproves nothing about equality. dict.fromkeys(keys, [])shares one list across all keys; appending through one key shows up under every key.- Reading
d[float('nan')]after storing under another NaN. Lookup needs equality with the stored key, and NaN never equals NaN.
Practice
The section mini-project covers this: diagnosing and fixing the seeded aliasing defect with a regression test.
Self-test
-
After
a = [1, [2]];b = a;c = copy.copy(a);b.append(3);b[1].append(4), what areaandc?ais[1, [2, 4], 3]becausebnames the same list.cis[1, [2, 4]]: its outer list was copied before the append, but it shares the inner list. -
Why does
hash((1, 2))work whilehash((1, [2]))raisesTypeError?Tuple hashing needs every item hashable. Alistis unhashable, so thetupleis unhashable despite being immutable. -
d = {};k = float('nan');d[k] = 1. Why doesd[float('nan')]raiseKeyError?Lookup compares by hash then equality, and NaN never equals NaN, so the fresh key never matches the stored one. -
Give a case where
x is yholds butx == yisFalse.x = y = float('nan'). Identity holds and equality isFalse. Apart from pathological__eq__definitions, identity implies equality.
Completion criterion
Stop when
You predict the alias, copy and NaN cases without running them, and your copy-cost numbers match a fresh timeit run.
Leads to
- 3.2 Argument binding, scope and closures Follow mutation across call boundaries and mutable defaults.
- 3.9 Reference counts, cycle collection and weak references Connect aliasing to object lifetimes and weak references.
Comes after
- 2.5 Make the experiment reproducible and explain its limitsAfter completing the first supervised ML project, deepen the Python object model and runtime behavior behind reliable code.
Resources for this unit
- Stas' Python Cookbook - selected path Run Part I on dictionaries, iteration, functions, closures, decorators, classes and context managers; Part II on modules, files, paths, environment and arguments; and Part IV on versions, dependencies, packaging and code quality. Finish by building one src-layout package from a blank directory.
- Python language reference Check Data model, Execution model, Import system and Exceptions whenever a claim depends on Python semantics. These pages are the authority when the course and a runtime observation disagree.
Also in this section
- Packaging Python Projects Follow the packaging tutorial once from a blank directory, using a src layout, pyproject.toml, an isolated build and a clean-environment installation test.
- AlgoMaster Python - structured revision Use Functions, Modules and Packages, OOP, Advanced OOP, Exception Handling, Iterators and Generators, Type Hints and Annotations only after the cookbook pass. Open it for recall practice or a diagnosed weak mechanism, not as a second complete course.
- dataclasses - data classes Read the
@dataclassparameters (eq,order,frozen,slots),field()withdefault_factory, frozen instances, post-init processing,replace,asdictandastuple. Use for the validated frozen dataclass in attributes-protocols-dataclasses. - typing - type hints and protocols Read Protocols for structural typing and
runtime_checkablelimits, Generics forTypeVargenerics, andassert_typefor narrowing checks. Use for theProtocoland narrowing parts of type-hints-protocols; PEP 695 syntax itself is in the language reference. - gc - cycle garbage collector Read the cycle collector interface:
collect,get_count,garbageanddisable/enable. Use for the cycle-collection and measurement parts of refcounting-gc-weakrefs. - weakref - weak references Read weak-reference objects (
refcallbacks,proxyobjects) and theWeakValueDictionaryexample. Use for the weak-reference part of refcounting-gc-weakrefs.