Variables
Names, assignment, and Python's dynamic typing
In Python, a variable is a name that references an object in memory. There's no let, var, or type declaration—assignment with = creates the binding. This simplicity is powerful, but it comes with subtleties you need to understand to avoid surprises.
Every snippet on this page runs in your browser—no setup required.
Real-world context: Variables everywhere
Variables are the foundation of every program. In a web app, you might have user_id, session_token, and request_data. In a data pipeline, rows, schema, and output_path. In a game, player_x, enemy_health, and score. Names let you give meaning to values and reuse them throughout your code.
Names are labels, not boxes
This is the most important mental model for Python. Variables are not containers that hold values—they're labels that point to objects in memory.
When you write x = [1, 2, 3], Python creates a list object and binds the name x to it. If you then write y = x, you're binding a second name to the same list object.
Mutating the object through one name is visible through all names that reference it:
Mutable default arguments gotcha
This label-not-box model is why mutable defaults in function signatures are dangerous:
def add_item(item, items=[]):
items.append(item)
return itemsThe empty list [] is created once when the function is defined, and every call that omits items will reference the same list object. We'll revisit this when we cover functions.
To create an independent copy of a list, use .copy() or the list() constructor:
Dynamic typing: freedom and footguns
Python names have no fixed type. You can rebind a name to a value of any type at any time.
This is convenient for rapid prototyping, but it means typos in variable names are not caught until runtime. In a statically typed language like Java or Go, usre_id = 123 (instead of user_id) would be a compile error. In Python, it's just a new variable.
Type annotations: the best of both worlds
Python 3.5+ supports optional type annotations:
user_id: int = 123
name: str = "Ada"These don't affect runtime behavior, but tools like mypy can check your code for type errors before you run it. In production codebases, type annotations are increasingly the norm—they catch bugs early and make code easier to understand.
Naming rules and conventions
A valid Python identifier:
- Starts with a letter (including Unicode letters like
é,你) or underscore_. - Continues with letters, digits, or underscores.
- Is case-sensitive:
Scoreandscoreare different names. - Cannot be a reserved keyword like
if,class,def,return,pass,lambda.
PEP 8 style conventions
PEP 8 is Python's style guide. Following it makes your code readable to other Python developers.
| Kind | Convention | Example |
|---|---|---|
| Variable, function | snake_case | user_count, fetch_data |
| Constant | UPPER_SNAKE_CASE | MAX_RETRIES, API_KEY |
| Class | PascalCase | HttpClient, UserProfile |
| Module file | snake_case.py | data_loader.py |
| Private (by convention) | leading _ | _internal_cache |
Why PEP 8?
Consistency makes code easier to read. When every Python project uses snake_case for functions and PascalCase for classes, you can glance at HttpClient() and know it's a class, or at fetch_data() and know it's a function—without reading the definition.
Multiple assignment patterns
Python supports several shorthand forms that make common patterns more concise.
Constants?
Python has no enforced const keyword. The convention is to use ALL_CAPS names and trust developers to leave them alone.
MAX_RETRIES = 3
TIMEOUT_SECONDS = 30
API_BASE_URL = "https://api.example.com"As of Python 3.8, the typing.Final annotation lets static type checkers enforce immutability:
from typing import Final
MAX_RETRIES: Final[int] = 3But at runtime, nothing stops reassignment. Python trusts you.
Challenges
Without using a temporary variable, swap the values of a and b in a single line so that a becomes "banana" and b becomes "apple".
Write a function is_valid_identifier(name) that returns True if name is a valid Python identifier that is not a reserved keyword, and False otherwise. Use the str.isidentifier() method and the keyword module.
Check your understanding
What does this code print?
nums = [1, 2, 3]
also_nums = nums
also_nums.append(4)
print(nums)
[1, 2, 3]
[1, 2, 3, 4]
A NameError
[4]
Which of the following is not a valid Python identifier?
user_count
_private
2nd_place
café
After this code runs, what is the value of x?
x = [10, 20]
y = x
y = y + [30]
[10, 20, 30, 30]
[10, 20]
[10, 20, 30]
An error
Next: a tour of Python's built-in data types.