09 / 12

What are Python's built-in data types, and how are they categorized (mutable vs. immutable)?

Difficulty: 3/10
Variables, Data Types & Memory Model, Mutable vs Immutable Types, Hashability

Built-in types and the mutable/immutable split

I group the built-ins by role: numeric (int, float, complex, bool), text and binary (str, bytes, bytearray, memoryview), sequences (list, tuple, range), mappings (dict), sets (set, frozenset), and the singleton NoneType. The categorization that matters in practice is mutability: can the object's own state change in place after creation? That is a property of the object, not of the variable name. Rebinding a name never mutates anything.

  1. 1

    Immutable: int, float, complex, bool, str, bytes, tuple, frozenset, range, NoneType

  2. 2

    Mutable: list, dict, set, bytearray (plus most user-defined class instances by default)

  3. 3

    Mutability drives hashability: only hashable objects can be dict keys or set members, and built-in mutable containers are deliberately unhashable

  4. 4

    bool is a subclass of int, so True + True == 2 and isinstance(True, int) is True

The 'why' behind the split: immutable objects can be safely shared, cached, interned and used as dictionary keys because their hash can never change. Mutable objects give you cheap in-place updates (list.append is amortized O(1)) at the cost of aliasing bugs. The trade-off shows up in everyday choices: tuple vs list for fixed records, frozenset vs set for constant lookup tables or dict keys, and str concatenation in a loop (creates new objects each time) vs ''.join or io.StringIO.

javascript

Common misconceptions: (1) 'a tuple is fully immutable' - it only guarantees its references never change, so a tuple holding a list is mutable in effect and not hashable; (2) 'x += 1 mutates the int' - it rebinds x to a new int; (3) mutable default arguments, which is the classic production bug from this topic. The fix is a None default and creating the list inside the function. Note that += on a list does mutate in place (via iadd), which is why a += b and a = a + b behave differently for lists.

Version notes: dict preserves insertion order as a language guarantee from 3.7 (an implementation detail in CPython 3.6). Dict/set views and the 3.9+ dict union operators do not change mutability rules. Type hints (list[int], etc.) are not enforced at runtime.

Scenario Questions

0-2 years experience

  1. 1Given the keys 42, 'abc', (1, 2), [1, 2] and (1, [2]), which can be used as dictionary keys and which raise TypeError? Explain each.
  2. 2A teammate claims s += 'x' inside a loop modifies the same string in place. What is actually happening, and what would you use instead for building a large string?

2-5 years experience

  1. 1A memoization decorator keyed on function arguments crashes with 'unhashable type: list' when callers pass lists. How would you fix it without changing callers, and what are the trade-offs?
  2. 2A 'constant' config stored as a tuple of dicts is changing at runtime according to a bug report. How would you track down the mutation and prevent it going forward?

5-8 years experience

  1. 1You are designing a value object such as Money or Point. Compare a frozen dataclass, a namedtuple and a class with __slots__ - which would you choose and why?
  2. 2During code review you find mutable default arguments in several places. How would you audit and fix this at scale across a large codebase, and which lint rules would you enable?

8+ years experience

  1. 1You are designing a public library API that accepts and returns large collections. How do you decide between returning a list, tuple, or a read-only view (e.g. MappingProxyType) to protect internal state without paying for defensive copies?
  2. 2A team claims their 'tuple of lists' is thread-safe because tuples are immutable. Evaluate that claim and propose a safe design, including what changes under the free-threaded build in 3.13+.

Follow-up Questions

  • Why is a tuple containing a list not hashable, and can you still use it in a set?
  • Why does Python allow a += b to behave differently from a = a + b for lists?
Share

Share via WhatsApp, X, Facebook, LinkedIn or copy link. Open Graph preview enabled.