← Home

005. Identifiers

Names must follow rules before they carry meaning

005. Identifiers

Aryan’s RAM monitor reads process names from a config file that users edit. A user types a field name like 2nd_process or mem-usage — and the monitor crashes when Python tries to use it as a variable.

He needs a validator: before accepting any user-supplied field name, check that it’s a legal Python identifier AND not a reserved keyword.

Rules for valid identifiers:

  • Must start with a letter (a–z, A–Z) or underscore (_)
  • Can contain letters, digits (0–9), underscores — nothing else
  • Cannot be a Python keyword

The broken code only does the syntax check but forgets to exclude keywords — so for and while pass as valid, which would crash the monitor later.


💡 Fun fact: Python 3 extended identifier rules to allow Unicode letters — so café, 名前, and Ärger are all valid variable names. PEP 3131 (2007) made this possible, meaning Python identifiers can represent names from almost any human language.

⚠️ Watch out: str.isidentifier() returns True for keywords like for and class — it only checks character rules, not reservation status. You must combine it with not keyword.iskeyword() for a complete validity check.

🤔 Think about it: _ (a single underscore) is a perfectly valid Python identifier used by convention for “throwaway” variables and in the REPL for the last result. What other single-character identifiers carry special meaning by convention in Python?

Learning objectives

  • Know the rules for valid Python identifiers
  • Use str.isidentifier() for syntax checking
  • Combine identifier check with keyword exclusion

Key concepts

  • identifiers
  • naming rules
  • keyword module

Try it

Concept detail

A Python identifier is any name used for variables, functions, classes, modules, etc. Rules: must start with a letter (a-z, A-Z) or underscore (_), followed by any combination of letters, digits (0-9), or underscores. Case-sensitive: myVar != myvar.

str.isidentifier() checks only the character-set rules. It returns True for keywords like ‘for’ and ‘class’ because those are syntactically valid name shapes — the parser rules them out separately.

For a complete validation you need BOTH checks: name.isidentifier() and not keyword.iskeyword(name)

The broken code passes the character-set check but skips keyword exclusion, so ‘for’, ‘while’, ‘class’ all appear valid. In the RAM monitor context, accepting ‘for’ as a config field name would cause a SyntaxError the moment that name was used in generated code.

Leading underscores carry convention meaning: _name (internal), __name (name-mangled in classes), name (dunder/magic). These are valid identifiers, not keywords.

Solution

import keyword

def is_valid_identifier(name):
    return name.isidentifier() and not keyword.iskeyword(name)

Tests

def test_valid_simple():
    assert is_valid_identifier("my_var") == True

def test_valid_with_underscore_prefix():
    assert is_valid_identifier("_private") == True

def test_invalid_starts_with_digit():
    assert is_valid_identifier("1abc") == False

def test_invalid_has_space():
    assert is_valid_identifier("my var") == False

def test_invalid_keyword():
    assert is_valid_identifier("for") == False

def test_invalid_keyword_while():
    assert is_valid_identifier("while") == False

def test_invalid_has_dash():
    assert is_valid_identifier("my-var") == False

Resources