Variables & Data Types
A variable is a box with a label taped on the front. Write age on the label, put 12 inside, and from now on anyone who asks for "age" gets 12 โ until you swap the contents. The box does not care what it held yesterday; it holds exactly one thing at a time. Data types are just box shapes: a whole-number box (integer), a decimal box (float), a text box (string), and a tiny switch that is only ever on or off (boolean). The shape matters because the machine handles each one differently โ you cannot pour "hello" into a number box and expect maths to work on it. One more Python secret: the label is a sticky note, not part of the box. Peel "x" off the number 5 and stick it on the word "five", and Python shrugs โ the LABEL moved to a different box; nothing broke. Some stricter languages (Java, C) glue each label to one box shape forever and refuse the swap; Python never glues.
Boxes with labels
step 1 / 5One box so far: the label says age, the contents say 12, and the box shape is "whole number".
Every built-in data type in Python (CPython 3.12, 64-bit)
| type | example | memory (sys.getsizeof) | mutable? | notes |
|---|---|---|---|---|
| int | 42 | 28 bytes small; +4 per extra 30 bits | no | ARBITRARY precision โ never overflows, just grows |
| float | 3.14 | 24 bytes | no | IEEE-754 double: 15โ17 significant digits; 0.1+0.2 != 0.3 |
| bool | True | 28 bytes | no | a subclass of int โ True == 1, so sum([True,True]) == 2 |
| complex | 2+3j | 32 bytes | no | two doubles: real + imaginary |
| str | "hi" | 49 + ~1โ4 bytes/char | no | Unicode; width adapts to the widest char (emoji cost 4/char) |
| bytes | b'hi' | 33 + 1 byte/char | no | raw 0โ255 values; what files and networks actually carry |
| NoneType | None | 16 bytes (one shared object) | no | the "no value" singleton; test with `is None` |
| list | [1, 2] | 56 + 8/slot (overallocates ~12%) | YES | the dynamic array of Module 2 |
| tuple | (1, 2) | 40 + 8/slot | no | immutable list; hashable, so usable as a dict key |
| dict | {'a': 1} | 64 empty; grows in powers of 2 | YES | the hash map of Module 2 |
| set | {1, 2} | 216 empty | YES | hash table without values; frozenset is its immutable twin |
| range | range(10**9) | 48 bytes regardless of length | no | lazy โ computes values on demand |
The rules
- Names bind, boxes don't exist twice: `b = a` copies a REFERENCE. For immutable values that's indistinguishable from a copy; for a list, both names now point at ONE list.
- Identifiers: letters, digits, underscores; can't start with a digit; case-sensitive (Age โ age); can't be a keyword (`if`, `class`, `for`โฆ). Convention: snake_case for variables, UPPER_CASE for constants.
- Immutable types (int, float, str, tuple, bool) can never change in place โ every "modification" builds a new object. That's why `s += "!"` in a loop is O(nยฒ).
- Only immutable (hashable) values may be dict keys or set members โ a list can't, a tuple can.
- Check types with isinstance(x, int), never `type(x) == int` (isinstance respects subclasses โ remember bool!).
- Integer division of big ints is exact โ Python ints never silently wrap around like C/Java ints (the Ariane 5 class of bug can't happen here).
See it yourself
import sys
print(sys.getsizeof(0)) # 28
print(sys.getsizeof(10**100)) # 72 โ the int GREW
print(sys.getsizeof("")) # 49
print(sys.getsizeof("hi")) # 51
print(sys.getsizeof([])) # 56
print(True + True) # 2 (bool is an int!)
print(0.1 + 0.2 == 0.3) # False โ floats approximateage = 12 # int price = 9.99 # float name = "Ada" # str lights_on = True # bool age = age + 1 # rebind: the box now holds 13 print(type(age)) # <class 'int'>
x = 5, then x = "five". In Python, what is x now?
Write next_age(age): store age + 1 in a variable and return it. Your first box.