Session 3 · A variable is a name, not a box#
📖 Based on the blog post Python Variable: Is a Container or a Reference?.
Tip
This is a deep dive. If you’re brand new, skim it now and come back after Session 7 (lists). It explains some of the most surprising bugs beginners hit.
The big idea#
In many languages a variable is a box that holds a value. In Python, a variable is a name tag tied to an object. The object lives somewhere in memory; the name just points to it. Two names can point to the same object.
Everyday example: your friend is “Priya” at college and “Chellam” at home. Two names, one person. If Priya gets a haircut, Chellam has the haircut too.
Everything is an object#
Numbers, text, lists, functions: all of them are objects.
for thing in [26, "Idly", [1, 2], print]:
print(type(thing).__name__, isinstance(thing, object))
int True
str True
list True
builtin_function_or_method True
Names point to objects: id()#
id() gives an object’s identity (in CPython, its memory address). A name has the same id as the
object it points to:
age = 26
print(id(age) == id(26))
True
Two names, one object#
a = [1, 2]
b = a # b points to the SAME list, it's not a copy
b.append(3)
print(a)
print(a is b)
[1, 2, 3]
True
is checks “same object?”; == checks “same value?”. To get a separate list, make a copy:
a = [1, 2]
b = a.copy() # or list(a), or a[:]
b.append(3)
print(a, b, a is b)
[1, 2] [1, 2, 3] False
Why don’t numbers and strings surprise us?#
Numbers, strings and tuples are immutable: they can’t change. x += 1 doesn’t change the
object 5; it makes a new object 6 and moves the name x to it:
x = 5
y = x
x += 1
print(x, y)
6 5
Lists, dictionaries and sets are mutable: changing them in place is seen through every name that points to them.
Where do names live? Namespaces#
Python keeps names in namespaces: dictionaries that map names to objects. Each function call gets its own local namespace; the module (your file) has a global one. Tying a name to an object is called binding.
def greet():
message = "Vanakkam"
print(locals()) # the function's own namespace
greet()
{'message': 'Vanakkam'}
How big is a variable?#
sys.getsizeof(x) returns the size of the object x points to, not of the name:
import sys
print(sys.getsizeof(10)) # an int object, in bytes
28
The name itself is just a reference (a pointer): 8 bytes on a 64-bit computer. We can estimate it: a list of 1,000 references to the same object is about 8,000 bytes bigger than an empty list.
import sys
size_of_one_reference = (sys.getsizeof([123] * 1000) - sys.getsizeof([])) / 1000
print(size_of_one_reference)
8.0
Summary#
A variable is a label for an object, not a container.
Variables have no type;
type(x)tells you the type of the objectxpoints to.Names live in namespaces; a reference is 8 bytes on 64-bit Python.
b = anever copies. Use.copy()when you need an independent object.
Common mistakes#
b = ato “back up” a list. Changingbchangesa. Useb = a.copy().Using
isto compare values.x is 1000may beFalseeven whenx == 1000. Use==for values; useisonly withNone(if x is None:).Default list arguments.
def add(item, bucket=[])shares one list between all calls. Usebucket=None, thenif bucket is None: bucket = [].
Hands-on exercises#
Exercise 1 · Predict. What does this print? x = [1]; y = x; y = [2]; print(x)
Answer
[1]. y = [2] doesn’t change the list; it moves the name y to a new list. Only
in-place changes (append, y[0] = …) affect the shared object.
Exercise 2 · Same or equal? Make two lists with the same values. Show that == is True but
is is False.
Solution
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b, a is b) # True False
Exercise 3 · Safe backup. Back up marks = [80, 90], change marks[0] to 85, and show the
backup still has 80.
Solution
marks = [80, 90]
backup = marks.copy()
marks[0] = 85
print(marks, backup) # [85, 90] [80, 90]
Exercise 4 · The shared default bug. Run this and explain the output:
def add_item(item, bucket=[]):
bucket.append(item)
return bucket
print(add_item("idly"))
print(add_item("dosa"))
Answer and fix
It prints ['idly'], then ['idly', 'dosa']. The default list is created once, when the
function is defined, and every call shares it. Fix:
def add_item(item, bucket=None):
if bucket is None:
bucket = []
bucket.append(item)
return bucket
Exercise 5 · Inside a function. Write def show(): x = 1; y = "a"; print(locals()). What do you
see, and what happens to those names after the function returns?
Answer
{'x': 1, 'y': 'a'}. The local namespace disappears when the function returns; if nothing else
points to those objects, Python frees them.