Back to Blog
Python

Python Variable Naming Rules and Conventions

python variable naming rules: Learn Python's identifier syntax, reserved keywords, and PEP 8 naming conventions to write variables that survive code review and product...

python identifiersPEP 8naming conventionscode qualitypython syntax
Editorial illustration of Python variable naming showing valid and invalid identifier examples with underscore and keyword symbols.

python variable naming rules requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

Python's variable naming rules sit at the boundary between what the interpreter accepts and what a codebase can sustain. The interpreter enforces a small set of syntax rules for identifiers; the wider ecosystem adds convention on top of that. Both matter when you are writing code that other developers will read, review, and modify.

What Makes an Identifier Legal

Python accepts identifiers built from letters, digits, and the underscore character. An identifier cannot begin with a digit, so 1st_value is a syntax error while first_value and _1st_value are valid. Python 3 also allows Unicode characters in identifiers, provided they are classified as letters or combining marks. That means café and 变量 are legal names, though most teams avoid them for readability and cross-platform consistency.

Case matters. user_id, User_Id, and USER_ID are three distinct variables. This is a common source of bugs when two names differ only in case, especially when one developer writes userID and another reads userid.

The underscore is not just decoration. A leading underscore signals intent: _internal is a convention for private or implementation-detail names. Two leading underscores, as in __private, triggers name mangling inside classes. Names with two leading and two trailing underscores, like __init__, are reserved for Python's special methods and attributes.

Reserved Keywords You Cannot Use

Python reserves a fixed set of keywords for the language itself. Attempting to use one as a variable name raises a SyntaxError at parse time, not at runtime. The current keyword set includes False, None, True, and, as, assert, async, await, break, class, continue, def, del, elif, else, except, finally, for, from, global, if, import, in, is, lambda, nonlocal, not, or, pass, raise, return, try, while, with, and yield.

The error appears immediately:

class = "example" # SyntaxError: invalid syntax

There is no workaround that makes class a valid variable name. The standard approach is to append an underscore: class_ or type_ are common patterns for parameters that mirror keyword names. This is why Django model fields and SQLAlchemy columns frequently use trailing underscores in Python code.

Shadowing Built-in Names

A more subtle problem is shadowing built-in names. list, dict, str, type, and id are not keywords, so assigning to them is legal:

list = [1, 2, 3]

The assignment succeeds, but it replaces the built-in list in the current scope. Any later code that calls list() to construct a new list will raise TypeError: 'list' object is not callable. The failure is often far from the assignment, which makes it hard to trace.

Shadowing is not always wrong. In a short function, a local variable named id is usually harmless. The problem appears when shadowing happens at module level or in a long-lived scope, because every subsequent use of that name in the module now refers to your variable. Linters such as pylint flag built-in shadowing with redefined-builtin, and most teams treat that as an error.

PEP 8 Conventions for Production Code

PEP 8 defines the naming conventions that most Python projects follow. Variables and functions use snake_case: lowercase words separated by underscores, as in user_count or get_connection. Classes use PascalCase, as in UserProfile or DatabaseConnection. Constants are written in UPPER_SNAKE_CASE, such as MAX_RETRIES or DEFAULT_TIMEOUT.

The distinction between constants and module-level variables is purely conventional; the interpreter does not enforce it. A module attribute named MAX_RETRIES can be reassigned without error. The uppercase form is a signal to readers that the value is intended to remain fixed for the lifetime of the process.

PatternExampleApplies to
snake_caseuser_countvariables, functions
PascalCaseUserProfileclasses
UPPER_SNAKE_CASEMAX_RETRIESconstants
_leading underscore_cacheprivate or implementation detail
__double underscore__tokenname-mangled attribute
__dunder____init__special methods

Private attributes and methods use a single leading underscore: _cache or _validate_input. This does not prevent access; it communicates that the name is implementation detail and not part of the public API. Tools like dir() and many IDEs will still show the name, but the convention tells callers not to rely on it.

Name Mangling with Double Leading Underscores

Two leading underscores inside a class trigger name mangling. The interpreter rewrites __secret to _ClassName__secret at compile time:

class Service: def __init__(self): self.__token = "abc" svc = Service() print(svc.__token) # AttributeError print(svc._Service__token) # "abc"

Name mangling exists to prevent accidental attribute collisions in inheritance hierarchies, not to provide real privacy. If a subclass defines __token as well, the two attributes do not collide because each is rewritten with its own class name. Outside the class, the mangled name is still accessible, so this is a protection against accidental shadowing, not a security boundary.

Naming Mistakes That Cost Time in Review

The most expensive naming mistakes are not syntax errors; they are names that mislead readers. A variable named data in a function that processes user records forces the reader to trace where it came from and what shape it has. user_records or pending_users communicates that directly. Similarly, single-letter names like l, O, and I are easy to confuse with 1 and 0 in many fonts, and l is visually close to 1 in most monospace fonts.

Names that encode type information are also fragile. user_list breaks when the implementation changes to a tuple or a generator. users is stable regardless of the underlying container. The same applies to dict_of_config; config says what the value means without tying it to a specific collection type.

Tooling That Enforces Naming Rules

Because naming conventions are not enforced by the interpreter, teams rely on static analysis. flake8 combines pycodestyle and pyflakes to report violations of PEP 8 naming rules, including invalid constant names and missing underscores. pylint adds deeper checks such as built-in shadowing and invalid attribute names. black does not enforce naming, but it removes formatting noise so that naming problems are easier to see in review.

Type hints interact with naming in a practical way. A variable annotated as user: User documents its expected type without embedding the type in the name. This reduces the pressure to write user_obj or user_instance; the annotation carries that information. When the type changes, the annotation changes in one place, while the name stays stable across call sites.

Renaming a variable across a large codebase is best done with an IDE refactoring tool rather than search-and-replace. A naive replace of user can hit username, user_id, and user_count, changing names that should stay intact. IDE refactoring understands scope boundaries and updates only the intended binding.

python variable naming rules: Practical Usage and Code Examp | RYUSLOG DEV