Skip to content

Naming Constraints

Python identifiers (variable names, function names, class names) must follow specific rules.

Basic Rules

First Character

Valid first characters: - Letters: A-Z, a-z - Underscore: _

Invalid first characters: - Digits: 0-9

# Valid
name = "Alice"
_private = 42
Name = "Bob"

# Invalid
# 2speed = 60      # SyntaxError
# 123var = "data"  # SyntaxError
# 1st_place = True # SyntaxError

Subsequent Characters

After the first character, you can use: - Letters: A-Z, a-z - Digits: 0-9 - Underscore: _

Not allowed anywhere: - Spaces - Hyphens: - - Special characters: @, #, $, etc.

# Valid
user123 = "Alice"
data_2024 = [1, 2, 3]
my_var_123 = True

# Invalid
# user-name = "Alice"  # SyntaxError
# user name = "Bob"    # SyntaxError
# user@host = "data"   # SyntaxError
# price$ = 100         # SyntaxError

Case Sensitivity

Python distinguishes between uppercase and lowercase.

Data = 100
data = 200
DATA = 300

print(f"{Data = }")  # Data = 100
print(f"{data = }")  # data = 200
print(f"{DATA = }")  # DATA = 300

Common mistake:

name = "Alice"
# Later...
print(Name)  # NameError: name 'Name' is not defined

Reserved Keywords

Python keywords cannot be used as identifiers.

import keyword
print(keyword.kwlist)

Common keywords: - Control flow: if, else, elif, for, while, break, continue - Functions: def, return, lambda, yield - Classes: class, pass - Logic: and, or, not, in, is - Constants: True, False, None - Exceptions: try, except, finally, raise - Imports: import, from, as - Context: with, async, await

# All invalid - SyntaxError
# class = "MyClass"
# def = 42
# for = [1, 2, 3]
# None = 10
# True = False

Special Patterns

Single Underscore

Used for throwaway variables.

# Loop counter you don't need
for _ in range(5):
    print("Hello")

# Unpacking values you don't need
_, status, _ = ("GET", 200, "OK")

Leading Underscore

Convention for private/internal names.

_private = 42
_internal_helper = lambda x: x * 2

Double Leading Underscore

Triggers name mangling in classes.

class MyClass:
    def __init__(self):
        self.__private = 42  # Becomes _MyClass__private

Dunder (Double Underscore Both Sides)

Reserved for special methods.

class MyClass:
    def __init__(self):
        pass

    def __str__(self):
        return "MyClass instance"

    def __len__(self):
        return 0

Edge Cases

Looks Invalid But Valid

# All underscores - valid
_ = 42
__ = "data"
___ = [1, 2, 3]
___private___ = True

Looks Valid But Reserved

# These look like valid names but are keywords
# None = 10     # SyntaxError
# True = False  # SyntaxError
# pass = 42     # SyntaxError

Validating Identifiers

Using isidentifier()

print("valid_name".isidentifier())   # True
print("2invalid".isidentifier())     # False
print("user-name".isidentifier())    # False
print("class".isidentifier())        # True (but reserved!)

Note: isidentifier() returns True for keywords, so additional checking is needed.

Checking for Keywords

import keyword

name = "class"
if keyword.iskeyword(name):
    print(f"'{name}' is a reserved keyword!")

Complete Validation

import keyword

def is_valid_identifier(name):
    """Check if name is a valid, non-reserved identifier."""
    return name.isidentifier() and not keyword.iskeyword(name)

# Test
print(is_valid_identifier("valid_name"))  # True
print(is_valid_identifier("class"))       # False (keyword)
print(is_valid_identifier("2bad"))        # False (starts with digit)
print(is_valid_identifier("user-name"))   # False (contains hyphen)

Batch Testing

import keyword

tests = [
    "valid_name",
    "_private",
    "__dunder__",
    "var123",
    "2invalid",
    "user-name",
    "class",
    "None",
]

for name in tests:
    valid = name.isidentifier()
    is_kw = keyword.iskeyword(name)

    if is_kw:
        status = "❌ Reserved keyword"
    elif valid:
        status = "✅ Valid"
    else:
        status = "❌ Invalid syntax"

    print(f"{name:15} {status}")

Output:

valid_name      ✅ Valid
_private        ✅ Valid
__dunder__      ✅ Valid
var123          ✅ Valid
2invalid        ❌ Invalid syntax
user-name       ❌ Invalid syntax
class           ❌ Reserved keyword
None            ❌ Reserved keyword

Quick Reference

Pattern Valid? Reason
name Standard identifier
_name Private convention
__name Name mangling
__name__ Dunder/magic
Name Capitalized
NAME Constant convention
name123 Digits after first char
name_var Underscore separator
2name Starts with digit
name-var Contains hyphen
name var Contains space
name@host Contains special char
class Reserved keyword
None Reserved keyword

Best Practices

  1. Keep names readable - Under 30 characters

    # Too long
    this_is_an_extremely_long_variable_name = 42
    
    # Better
    max_timeout = 30
    

  2. Use snake_case for variables and functions

    user_name = "Alice"
    def calculate_total():
        pass
    

  3. Use UPPER_CASE for constants

    MAX_SIZE = 100
    DEFAULT_TIMEOUT = 30
    PI = 3.14159
    

  4. Use PascalCase for classes

    class UserAccount:
        pass