Warming up the neural circuits...
By the end of this chapter you will:
Every program works with data. Variables are how you name and store that data. In Python, variables are more flexible than in languages like Java or C — you don't declare types, and a variable can hold any kind of value. That flexibility is powerful, but it causes bugs if you don't understand what's happening under the hood.
This chapter teaches you the mental model: variables are names bound to objects. Once you internalize that, Python's behavior makes sense instead of feeling like magic.
Python has five core types. Click each type to see its properties and use cases:
age = 30
temperature = -5
population = 8_000_000 # Underscores for readability
print(type(age)) # <class 'int'>Properties:
int42, -5, 0, 1_000_000+, -, *, //, %, **Key facts:
_) to separate thousands: 1_000_000 is the same as 1000000.price = 19.99
pi = 3.14159
scientific = 1.5e10 # 1.5 × 10^10
print(type(price)) # <class 'float'>Properties:
float3.14, -0.5, 1.5e10+, -, *, /, **Key facts:
0.1 + 0.2 is not exactly 0.3 (see Chapter 5).round() to control precision: round(3.14159, 2) → 3.14.name = "Alice"
greeting = 'Hello'
multiline = """This is
a multiline
string."""
print(type(name)) # <class 'str'>Properties:
str"hello", 'world', """multiline"""+ (concatenation), * (repetition), [] (indexing)Key facts:
is_active = True
has_permission = False
print(type(is_active)) # <class 'bool'>Properties:
boolTrue, Falseint (True = 1, False = 0)and, or, not, ==, !=Key facts:
True is 1, False is 0.True + True → 2. (Yes, really.)result = None
user = get_user() # Might return None if user doesn't exist
print(type(result)) # <class 'NoneType'>
print(result is None) # TrueProperties:
NoneTypeNoneis None (not == None)Key facts:
None is a singleton — there's only one None object in memory.is None to check for it, not == None.None by default.Think of variables as sticky notes attached to objects:
x = 42 puts a sticky note "x" on the object 42y = x puts another sticky note "y" on the same objectx = 100 moves the "x" note to a new object 10042This explains why y = x; x = 100; print(y) prints 42 — y still points to the original object.
In C or Java, a variable is a box in memory that holds a value. In Python, a variable is a name that refers to an object. The object lives somewhere in memory; the name is just a pointing to it.
x = 42This doesn't mean "put 42 in a box called x." It means "create an object 42 in memory, and bind the name x to it."
Why this matters:
x = 42
y = x
x = 100
print(y) # 42, not 100If variables were boxes, y = x would copy the value, and changing x wouldn't affect y. That's exactly what happens — but not because values are copied. It's because y is bound to the same object as x initially, and x = 100 rebinds x to a new object. y still points to the original 42.
Mental model: Think of variables as sticky notes attached to objects. x = 42 puts a sticky note labeled "x" on the object 42. y = x puts another sticky note "y" on the same object. x = 100 moves the "x" note to a new object 100. The "y" note stays on 42.
Python has five core types you'll use constantly:
Whole numbers, positive or negative. No decimal point.
age = 30
temperature = -5
population = 8_000_000 # Underscores for readability (Python 3.6+)
print(type(age)) # <class 'int'>Key facts:
_) to separate thousands: 1_000_000 is the same as 1000000.Numbers with a decimal point. Used for fractions, measurements, and anything that isn't a whole number.
price = 19.99
pi = 3.14159
scientific = 1.5e10 # 1.5 × 10^10
print(type(price)) # <class 'float'>Key facts:
0.1 + 0.2 is not exactly 0.3 (see Chapter 5 for the full story).round() to control precision: round(3.14159, 2) → 3.14.Text. Sequences of Unicode characters enclosed in quotes.
name = "Alice"
greeting = 'Hello'
multiline = """This is
a multiline
string."""
print(type(name)) # <class 'str'>Key facts:
"hello"[0] = "H" raises an error.Two values: True and False. Used for conditions and logic.
is_active = True
has_permission = False
print(type(is_active)) # <class 'bool'>Key facts:
True is 1, False is 0.True + True → 2. (Yes, really.)None are "falsy" (covered in Chapter 6).None is Python's "nothing" value. It's used to represent the absence of a value, like null in other languages.
result = None
user = get_user() # Might return None if the user doesn't exist
print(type(result)) # <class 'NoneType'>
print(result is None) # TrueKey facts:
None is a singleton — there's only one None object in memory.is None to check for it, not == None.None by default.type() returns the type of an object:
x = 42
print(type(x)) # <class 'int'>
y = "hello"
print(type(y)) # <class 'str'>isinstance() checks if an object is an instance of a type (or a subclass):
x = 42
print(isinstance(x, int)) # True
print(isinstance(x, str)) # False
# isinstance() also checks subclasses
print(isinstance(True,When to use which:
type() for debugging and introspection.isinstance() for type checks in your code — it's more flexible and handles inheritance.Python can convert between types explicitly using type constructors:
int() — convert to integer:
int("42") # 42
int(3.99) # 3 (truncates, doesn't round)
int(True) # 1
int(False) # 0float() — convert to float:
float("3.14") # 3.14
float(42) # 42.0
float("inf") # inf (infinity)str() — convert to string:
str(42) # "42"
str(3.14) # "3.14"
str(True) # "True"
str(None) # "None"bool() — convert to boolean:
bool(0) # False
bool(1) # True
bool("") # False (empty string)
bool("hello") # True (non-empty string)
bool(None) #Conversion failures:
int("hello") # ValueError: invalid literal for int() with base 10: 'hello'
float("abc") # ValueError: could not convert string to float: 'abc'Always handle conversion errors with try/except (covered in L1) or validate first.
Python is dynamically typed — variables don't have types; objects do. A variable can refer to any type of object, and it can change types.
x = 42 # x refers to an int
x = "hello" # x now refers to a str
x = [1, 2, 3] # x now refers to a listThis is legal in Python. In Java or C, you'd get a compile error.
Why this is powerful:
Why this is dangerous:
x + 1 works if x is an int, but fails if x is a string.The pragmatic solution: Use type hints (covered in L2) to annotate types without enforcing them. This gives you the flexibility of dynamic typing with the safety of static typing.
Data processing: Variables hold data from files, APIs, and databases:
user_id = 12345 # int
username = "alice" # str
balance = 100.50 # float
is_verified = True # bool
middle_name = None # NoneTypeConfiguration: Variables store settings:
DEBUG = True
MAX_RETRIES = 3
API_URL = "https://api.example.com"
TIMEOUT = 30.0tracking: Variables track program state:
count = 0
is_running = True
last_error = None| Mistake | Why it's wrong | Fix |
|---|---|---|
TypeError: can only concatenate str (not "int") to str | You tried to add a string and an int: "Age: " + 30 | Convert the int to a string: "Age: " + str(30) or use an f-string: f"Age: {30}" |
NameError: name 'x' is not defined | You used a variable before assigning it. | Assign the variable first: x = 5 |
x = "5"; print(x + 3) → TypeError | x is a string, not an int. You can't add a string and an int. | Convert x to an int: int(x) + 3 |
if x == None: | None is a singleton. Use is for identity checks. | |
Type hints: In production code, use type hints to annotate variables and function signatures:
def greet(name: str) -> str:
return f"Hello, {name}"
user_id: int = 12345Type hints are optional and not enforced at runtime, but tools like mypy can check them statically. We'll cover type hints in L2.
Constants: Python doesn't have true constants (values that can't be changed). By convention, constants are written in ALL_CAPS:
MAX_RETRIES = 3
API_URL = "https://api.example.com"These can still be reassigned, but the convention signals intent.
Memory management: Python uses reference counting and garbage collection. You don't need to manually free memory — when no names refer to an object, it's automatically cleaned up.
type().x = 10; y = x; x = 20; print(y) print? Why?x = 10; y = x; x = 20; print(y)type(x) and isinstance(x, int)? When would you use each?def get_length(value: str) -> int: return len(value)
</Solution>
---
## Interview Questions
<Callout type="tip" title="Interview Questions">
**Beginner:**
1. **"What's the difference between a variable in Python and a variable in C?"**
> In C, a variable is a box in memory that holds a value of a specific type. In Python, a variable is a name that refers to an object. The object has a type, not the variable. A Python variable can refer to any type of object.
2. **"What are the five core types in Python?"**
> `int` (integers), `float` (floating-point numbers), `str` (strings), `bool` (booleans: `True`/`False`), and `None` (the absence of a value).
**Senior:**
1. **"Why is `True + True` equal to `2` in Python?"**
> `bool` is a subclass of `int`. `True` is equivalent to `1`, and `False` is equivalent to `0`. So `True + True` is `1 + 1`, which is `2`.
2. **"What's the difference between `type(x) == int` and `isinstance(x, int)`?"**
> `type(x) == int` checks if `x` is exactly an `int`. `isinstance(x, int)` checks if `x` is an `int` or a subclass of `int`. Since `bool` is a subclass of `int`, `isinstance(True, int)` returns `True`, but `type(True) == int` returns `False`. Use `isinstance()` for type checks — it's more flexible and handles inheritance.
</Callout>
---
## Summary
- Variables are names bound to objects, not boxes that hold values.
- The five core types: `int`, `float`, `str`, `bool`, `None`.
- Use `type()` for debugging, `isinstance()` for type checks.
- Convert types explicitly with `int()`, `float()`, `str()`, `bool()`.
What are the five core types in Python?
Assuming float is exact | Floats are approximations. 0.1 + 0.2 is not exactly 0.3. | Use round() or the decimal module for precise arithmetic |