Skip to Content

Variables & Dynamic Typing

When to use

Bind a name to a value; rebind later if its meaning changes.

Analogy

Sticky note pointing at a box - the note can move, but the box itself does not.

Data-flow diagram

x -- points to --> object(42)
  |
  | reassign x = 'hello'
  v
x -- now points to --> object('hello')
  (old int is GC'd)

Deep explanation

Python variables are NAMED REFERENCES to objects on the heap. Assignment rebinds the name, never mutates the original value (ints, strings, tuples are immutable). Dynamic typing means the same name can refer to a list, then a dict, then a generator - type hints help tools catch mismatches before runtime. Multiple names can share one object; mutating through one is visible to the other.

Examples

Example 1

x = 42
x = 'hello'
print(type(x).__name__)

x is just a label; type() reflects the current object’s class.

Example 2

a = [1, 2, 3]
b = a          # b is NOT a copy
b.append(4)
print(a)        # [1, 2, 3, 4]

b = a creates an alias, not a copy; both names point to the SAME list.

Example 3

import sys
nums = [1, 2, 3]
print(sys.getrefcount(nums))   # 2 or 3 (incl. arg)

Variables are references; CPython keeps a refcount dropped to zero on last unbind.

Common mistake

Tying variable identity to type. Use is None only for None/True/False/sentinel singletons; reserve == for value comparison. CPython interns small ints (-5..256) and short strings, so is accidentally returns True for them at small scales - and incorrectly False at 257 or longer strings.

Key takeaway

Names are references, not boxes. is for identity/sentinels; == for values. Remember mutables are SHARED by aliasing.

Production Failure Playbook

Failure scenario 1: shared-mutable-default-arg

Failure scenario 2: shadowing-builtins