Data Types
Python has no "variable boxes". It has objects on the heap and names bound to them by reference. Assignment never copies an object — it binds one more name to the very same object, and every built-in type's behaviour follows from that model. An object is either mutable (list, dict, set), in which case an edit through one name is visible through all the others, or immutable (int, str, tuple, frozenset), in which case any "edit" actually builds a new object and rebinds the name.
The second split is hashability. A dict key and a set element must have a stable hash consistent with __eq__; that is why mutable types cannot go there, and why a hash table looks up in O(1) where a list honestly scans in O(n). The third axis is data representation. float stores a binary fraction, so 0.1 + 0.2 is not 0.3; str stores Unicode code points, not bytes and not "characters" in the human sense. The traps — is instead of ==, aliasing of nested lists, mutating a key after insertion, bool being a subclass of int — are worked through in the layers below.
Topic map
- Sequences — what makes a type a sequence — order,
__getitem__by integer index, and__len__. - Slicing —
s[start:stop:step]returns a new object, and out-of-range bounds are clamped, not errors. - Negative indexing —
-1is the last element, and why it is not "a countdown with an off-by-one". - Mutability — in-place change versus rebinding a name, and why they are different operations.
- list versus tuple — more than "you cannot change it" — different memory, growth strategy and hashability.
- Tuple unpacking — positional binding, the starred remainder, and nested targets.
- Hashability — the
__hash__/__eq__contract and why a mutable object breaks it. - The hash table inside — CPython's sparse table, probing on collision, and resizing.
- Dictionary keys — what qualifies as a key, and what happens if you mutate one after insertion.
- Mappings — the
Mappinginterface,get/setdefault,defaultdictandCounterinstead ofKeyError. - Dictionary views —
keys/values/itemsare live windows, not snapshots. - Sets — uniqueness through hashing, set algebra, and
frozensetas a key. - Identity — is versus == — the small-int cache and string interning make
islook deceptively "correct". - References and aliasing —
b = adoes not copy, and[[0]*3]*3makes three references to one list. - Sequence comparison — element-wise lexicographic ordering, and when
TypeErrorfires. - Operators and their results —
and/orreturn an operand, comparisons chain, and+=is not always+. - bool as a subclass of int —
True == 1,True + True == 2, and how1,True,1.0collapse into one key. - Floating point — the binary representation,
math.isclose, andDecimalfor money. - Strings — immutability, code points versus graphemes, and quadratic concatenation.
- str, bytes and encodings — text versus octets,
encode/decode, and no implicit conversions. - String methods — every one returns a new object;
split,stripandjoinwith their traps. - f-strings — replacement fields, the format spec,
!r,=, and escaping{{. - Comprehensions — a comprehension versus a loop versus a lazy generator expression.
- Operation complexity — the cost of indexing,
in,insert(0), and whydictbeatslist.
Common mistakes and traps
| Mistake | Consequence |
|---|---|
Comparing numbers or strings with is | It accidentally works on small ints and literals, then silently returns False for values that came from input or arithmetic |
Thinking b = a copies a collection | Both names point at one object — an edit via b shows up via a; you need a.copy(), or copy.deepcopy for nested structures |
Building a matrix as [[0] * 3] * 3 | The outer multiplication replicates a reference to one list — writing into one row changes them all |
Expecting 0.1 + 0.2 to equal 0.3 | The binary representation gives 0.30000000000000004; compare via math.isclose, and hold money in Decimal or in cents |
| Using a mutable object as a key, or mutating a key after insertion | list and dict keys raise TypeError, and mutating a key changes its hash — the entry stays in the table but stops being findable |
| Removing items from a list while iterating over it | Indices shift under the iterator and elements get skipped; iterate a copy or rebuild with a comprehension |
Testing membership with in over a list in a hot loop | The list scans in O(n) — on tens of thousands of items that is four orders of magnitude slower than a set |
Treating bool as a separate type unrelated to numbers | bool is a subclass of int, so True + True == 2 and {1: 'a', True: 'b'} collapses into one key |
What interviews check
This is the most common first-screen topic. What is probed is not a memorized list of types but whether you have a model. The classic scenario is a short aliasing snippet (b = a, [[0]*3]*3, t[0] += [100]) where you must predict the output and explain it in terms of references and mutability. Next comes is versus == on 256 and 257; a correct answer names the small-integer cache and then adds the important part — never rely on is for values. The third mandatory question is why dict beats list at lookup; the expected words are "hash table", "O(1) on average" and "the element must be hashable".
From there the interview branches by level. The junior set is list versus tuple, slicing, negative indices, unpacking, zip/enumerate, 0.1 + 0.2. Middle gets str versus bytes, dictionary views, sequence comparison, the behaviour of and/or, and debugging tasks such as a KeyError in a word counter. Senior level is CPython's hash-table internals and the trick question {True: 'a', 1: 'b', 1.0: 'c'}. The typical mistake is the same at every level — the answer is phrased as "a variable contains a value" instead of "a name refers to an object", after which the candidate cannot explain a single observed effect.