3.0 What This Teaches
This tutorial covers how Python handles variables and types:
- Dynamic typing - no declarations needed
- Built-in types: int, float, bool, str, None
- Names, objects, and assignment
- Type annotations for documentation and tooling
- Multiple assignment and tuple unpacking
3.1 Dynamic Typing
Python is dynamically typed - you don't declare types, and the same name
can hold different types at different times:
x = 42 # x holds an int
x = "hello" # x now holds a str - perfectly valid
x = [1, 2, 3] # x now holds a list
This differs from C# and Rust where a variable's type is fixed at compile time.
In Python, types belong to objects, not names. The name x is just
a label that can point to any object.
3.2 Built-in Types
| Type | Example | Notes |
| int | 42, -7, 1_000_000 | Arbitrary precision; never overflows |
| float | 3.14, 1.5e10 | 64-bit IEEE 754 double precision |
| bool | True, False | Subtype of int; True == 1, False == 0 |
| str | "hello", 'world' | Immutable Unicode text |
| NoneType | None | The absence of a value; like null in other languages |
Use type(x) to check the type of any object. Use isinstance(x, int)
to test whether an object is an instance of a type.
3.3 Names and Objects
Assignment binds a name to an object. Multiple names can point to the same object:
a = [1, 2, 3]
b = a # b points to the same list object
b.append(4)
print(a) # [1, 2, 3, 4] - a sees the change
a = [10, 20] # a now points to a new list; b still points to original
print(b) # [1, 2, 3, 4]
This is important for mutable types (lists, dicts). Immutable types (int, str, tuple)
cannot be changed in place, so sharing them is safe.
3.4 None
None is the singleton that represents "no value". It is the default
return value of functions that don't explicitly return anything. Test for it with
is, not ==:
result = None
if result is None:
print("no result yet")
# Functions return None implicitly
def do_nothing():
pass
x = do_nothing()
print(x) # None
3.5 Type Annotations
Python 3.5+ supports optional type annotations. They don't affect runtime behavior
but help IDEs and tools like mypy catch type errors early:
name: str = "Alice"
age: int = 30
score: float = 88.5
active: bool = True
def greet(name: str, greeting: str = "Hello") -> str:
return f"{greeting}, {name}!"
For nullable values (Python equivalent), use str | None (Python 3.10+)
or Optional[str] from the typing module.
3.6 Multiple Assignment and Unpacking
a, b = 1, 2 # tuple unpacking
a, b = b, a # swap without a temp variable
first, *rest = [1, 2, 3, 4] # starred assignment
print(first) # 1
print(rest) # [2, 3, 4]
x = y = z = 0 # assign same value to multiple names
The swap idiom a, b = b, a is idiomatic Python. The right side is
fully evaluated before any assignment happens.
3.7 Example - All Together
# Variables - demonstrates Python's type system.
count: int = 10
ratio: float = 3.14
flag: bool = True
name: str = "Alice"
missing = None
print(type(count), count)
print(type(ratio), ratio)
print(isinstance(flag, bool), isinstance(flag, int)) # True True (bool is int subtype)
print(name.upper())
print(missing is None)
x, y = 3, 4
x, y = y, x # swap
print(f"x={x}, y={y}")
Expected output:
<class 'int'> 10
<class 'float'> 3.14
True True
ALICE
True
x=4, y=3
3.8 Exercise
Exercise
- Assign an
int, float, str,
bool, and None to separate variables. Print each
with type().
- Try reassigning the same name to each type in turn - observe that Python
accepts it without error.
- Use tuple unpacking to swap two values in a single line. Confirm the swap
by printing before and after.
3.9 Common Mistakes
Using == to compare with None
x = None
if x == None: # works but wrong style
pass
if x is None: # correct - identity check, not equality
pass
is None tests identity (same object in memory). == None
tests equality and can be overridden by custom __eq__ methods.
Always use is None.
Integer division vs float division
print(7 / 2) # 3.5 (true division)
print(7 // 2) # 3 (floor division)
print(7 % 2) # 1 (modulo)
In Python 3, / always produces a float. Use //
for integer (floor) division.
Mutating a shared object unintentionally
Assigning a list to two names does not copy it. Both names point to the same object.
Use b = a.copy() or b = list(a) to get an independent copy.
3.10 Key Terms
| Term | Meaning |
| dynamic typing | Types belong to objects, not names; type can change at runtime |
| int | Arbitrary-precision integer; no overflow in Python |
| float | 64-bit IEEE 754 floating-point number |
| bool | True or False; a subtype of int |
| None | Singleton representing the absence of a value |
| type annotation | Optional hint written as name: type; checked by mypy not Python |
| isinstance() | Tests if an object is an instance of a type or its subtype |
| tuple unpacking | Assigning multiple names from a tuple in one statement |