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