Python Tutorial
Python Variable Names
A good name is documentation you get for free. Python only enforces a few rules — the rest is habit that makes your code readable to the next person (often future you).
The rules Python enforces
Only four rules are actually checked by the interpreter. Break one of these and you get a SyntaxErrorbefore the program even runs:
- A name must start with a letter (a–z, A–Z) or an underscore
_. - The rest of the name may contain letters, digits, and underscores — nothing else. No spaces, no dashes.
- Names are case-sensitive:
age,Age, andAGEare three different variables. - A name cannot be one of Python's reserved keywords (
if,class,return, …).
Example — all of these are legal
myvar = 1
my_var = 2
_my_var = 3
myVar = 4
MYVAR = 5
myvar2 = 6Example — each of these is a SyntaxError
2myvar = 7 # cannot start with a digit
my-var = 8 # a dash is read as "my minus var"
my var = 9 # spaces separate tokens
class = 10 # class is a reserved keywordLegal, but a bad idea
Some names Python accepts happily yet you should still avoid, because they quietly reassign something you'll want later. The most common trap is shadowing a built-in — using a name likelist, str, sum, id, or type for your own value.
Example — shadowing list breaks it
list = [1, 2, 3] # legal: 'list' now points at your list
nums = list(range(5)) # TypeError: 'list' object is not callableNothing is wrong on the first line, so the error shows up far away and looks mysterious. Pickitems or numbers instead and the built-in stays intact.
Not sure whether a word is a keyword? Ask Python: import keyword; keyword.iskeyword("class")returns True. Editors also colour keywords differently — a name that lights up likereturn is a warning sign.
Choose names that describe the value
Single letters are fine for a throwaway loop counter or a coordinate, but for anything that lives more than a couple of lines, spell it out. Code is read far more often than it is written.
| Prefer | Avoid | Why |
|---|---|---|
total_price | tp | The reader shouldn't decode abbreviations. |
user_count | n2 | Numbers on names say nothing about meaning. |
is_active | flag | Booleans read well as is_/has_ questions. |
i, x, y | the_index | Short is fine for tiny, obvious scopes. |
Multi-word names: pick a style and keep it
When a name needs more than one word, you have to join them somehow. Three conventions are common; Python's official style guide, PEP 8, has a clear preference.
my_variable_name = "snake_case" # PEP 8: use this for variables & functions
myVariableName = "camelCase" # common in JavaScript/Java, not Python
MyVariableName = "PascalCase" # reserve this for class names
MAX_RETRIES = 5 # ALL_CAPS for constants that never changeThe rule of thumb: snake_case for variables and functions, PascalCase for classes, and UPPER_SNAKE_CASE for constants. Consistency matters more than the choice itself.
A note on underscores
Leading and trailing underscores carry convention, not enforcement — Python won't stop you, but other programmers read them as signals:
| Name | Convention it signals |
|---|---|
_temp | "Internal — don't rely on this from outside." |
_ | "A value I'm deliberately ignoring," e.g. for _ in range(3): |
__dunder__ | Reserved by Python (e.g. __init__) — don't invent your own. |
Try It Yourself
Exercise: Three of these five names are problematic. Which, and why?
total = 0
2nd_place = "silver"
str = "hello"
first_name = "Ada"
user-id = 7Show answer
2nd_place is a SyntaxError (starts with a digit). user-id is aSyntaxError (the dash is a minus sign). str = "hello" is legal but shadows the built-in str() — rename it to something like greeting. total andfirst_name are both fine.
Key Takeaways
- Start with a letter or underscore; letters, digits, and underscores only; case matters; no keywords.
- Avoid names that shadow built-ins like
list,str, orsum. - Use
snake_casefor variables,PascalCasefor classes,ALL_CAPSfor constants. - Descriptive names are free documentation — spell things out beyond tiny scopes.
📘 Real-World Deep Dive
Names are how you communicate intent to the next reader. PEP 8 conventions plus a few defensive rules (no built-in shadows, prefer long-but-clear names) eliminate dozens of confusing bugs every project hits.
Real-Life Scenario
Reviewing a small ETL pipeline: rename variables so each conveys its type, its role in the pipeline, and its scope.
Real-Life Example
from dataclasses import dataclass
# BAD
xs = [r for r in raw if r["s"] == 1]
y = sum(float(x["amt"]) for x in xs)
# GOOD
STATUS_ACTIVE = 1
active_rows = [row for row in raw_rows if row["status"] == STATUS_ACTIVE]
total_amount = sum(float(row["amount"]) for row in active_rows)
@dataclass(frozen=True)
class LoadedEtl:
source: str
rows: int
total_amount: float
report = LoadedEtl(
source="orders.csv",
rows=len(active_rows),
total_amount=total_amount,
)
print(report)Expected Output
LoadedEtl(source='orders.csv', rows=187, total_amount=12450.75)Common mistakes
- Shadowing builtins (
list,dict,id,type) silently breaks later code in the file. - Names that differ only by case (
Uservs.user) cause cross-module confusion. - Single-letter names (
l,I,O) look like1/0— restrict them to short-lived loops.
🚀 Performance & Best Practices
- Long descriptive names cost roughly zero at runtime; pick clarity first.
- Use
SCREAMING_SNAKE_CASEfor module-level constants so they grep out as obviously non-mutables. - Hybrid prefixes (
is_*,_private) communicate intent without needing a docstring.
🧪 Try It Yourself
- Install
ruff check --select Nand clean up every pep8-naming violation. - Rename the
xs/y/datavariables in a real script to self-explanatory names. - Add a verify step that no
Pythonbuiltin name is shadowed at module level.