The str.isidentifier() method returns True if the string conforms to Python's lexical syntax rules for variable, function, class, or module names according to PEP 3131. A valid identifier must begin with an underscore (_) or letter, followed by letters, digits, or underscores, and must contain at least one character.
The Keyword Trap: isidentifier() validates lexical pattern syntax, not semantic validity. Reserved keywords like "def", "class", and "for" return True under isidentifier(). To ensure a string can safely be bound as a variable, pair it with keyword.iskeyword(name).
The isidentifier() method is a built-in string method and takes no parameters:
is_valid_syntax = string.isidentifier()
| Parameter | Type | Description |
|---|---|---|
| None | — | Takes zero arguments. Passing arguments raises TypeError. |
| Return Value | bool |
Returns True if the string is lexically a valid Python identifier; otherwise False. |
In Python, valid identifiers can consist of letters, underscores, and numbers, but they cannot begin with a digit or contain punctuation:
# Valid Python identifiers (return True)
print("user_name".isidentifier()) # True (snake_case)
print("UserScore".isidentifier()) # True (PascalCase)
print("_private_var".isidentifier()) # True (leading underscore)
print("__init__".isidentifier()) # True (dunder name)
print("item2".isidentifier()) # True (digits allowed after first character)
# Invalid identifier patterns (return False)
print("2item".isidentifier()) # False: cannot begin with a digit
print("user-name".isidentifier()) # False: hyphen '-' is subtraction operator
print("user name".isidentifier()) # False: whitespace is forbidden
print("order.id".isidentifier()) # False: dot '.' is attribute access
print("user@domain".isidentifier()) # False: symbols are forbidden
print("".isidentifier()) # False: empty string is not an identifier
Output:
True
True
True
True
True
False
False
False
False
False
False
A frequent beginner mistake is assuming isidentifier() guarantees a string can be used as a variable name. Because Python reserved keywords follow identifier syntax rules, they evaluate to True:
import keyword
# Keywords evaluate to True under isidentifier()
print("'class'.isidentifier():", "class".isidentifier()) # True
print("'def'.isidentifier(): ", "def".isidentifier()) # True
print("'for'.isidentifier(): ", "for".isidentifier()) # True
print("'None'.isidentifier(): ", "None".isidentifier()) # True
# Production function: verify syntax AND check keyword collision
def is_usable_variable_name(name):
return name.isidentifier() and not keyword.iskeyword(name)
print("\n'class' usable:", is_usable_variable_name("class")) # False
print("'user_1' usable:", is_usable_variable_name("user_1")) # True
Output:
'class'.isidentifier(): True
'def'.isidentifier(): True
'for'.isidentifier(): True
'None'.isidentifier(): True
'class' usable: False
'user_1' usable: True
match and case are soft keywords. They are reserved only inside pattern matching syntax and can still be used as regular variable names: keyword.iskeyword("match") returns False, while keyword.issoftkeyword("match") returns True.
Under PEP 3131, Python 3 supports international character sets in identifiers. Characters from world alphabets (Greek, Cyrillic, Chinese, etc.) are valid identifiers:
# Scientific and mathematical Greek identifiers
print("θ".isidentifier()) # True (Theta)
print("α_beta".isidentifier()) # True
# International language variable names
print("скорость".isidentifier()) # True (Russian: speed)
print("数据_1".isidentifier()) # True (Chinese: data_1)
print("résumé".isidentifier()) # True (French accented letters)
# Emoticons, symbols, and circled numbers are NOT valid identifiers
print("🚀".isidentifier()) # False
print("①".isidentifier()) # False
Output:
True
True
True
True
True
False
False
The table below summarizes how Python's string checking methods handle letters, numbers, underscores, and keywords:
| String | isidentifier() |
isalnum() |
keyword.iskeyword() |
Can be Variable Name? |
|---|---|---|---|---|
"score" | True | True | False | Yes |
"score_1" | True | False (has _) | False | Yes |
"_temp" | True | False (has _) | False | Yes |
"1st_item" | False (digit start) | False | False | No |
"class" | True | True | True | No (Reserved) |
"user-id" | False (has -) | False | False | No |
"" (empty) | False | False | False | No |
When mapping external API payloads or JSON schemas onto Python objects using setattr(), invalid identifier names prevent dot-access (obj.field) and cause runtime errors with namedtuple or dataclass:
import keyword
class DynamicRecord:
def set_field(self, key, value):
if not key.isidentifier() or keyword.iskeyword(key):
raise ValueError(f"Cannot set invalid attribute name: '{key}'")
setattr(self, key, value)
rec = DynamicRecord()
rec.set_field("total_count", 42)
print("total_count:", rec.total_count)
try:
rec.set_field("1st_place", "Gold")
except ValueError as e:
print("Error caught:", e)
Output:
total_count: 42
Error caught: Cannot set invalid attribute name: '1st_place'
Database columns often contain spaces or symbols (e.g. "Order Total ($)"). Code generators sanitize them into valid Python identifiers:
import re
import keyword
def to_valid_identifier(raw_name):
# Replace non-alphanumeric chars with underscore
clean = re.sub(r'\W+', '_', raw_name.strip()).strip('_')
# Prepend underscore if starts with digit or is empty
if not clean or clean[0].isdigit():
clean = f"_{clean}"
# Append trailing underscore if matches reserved keyword
if keyword.iskeyword(clean):
clean = f"{clean}_"
return clean
print(to_valid_identifier("Order Total ($)")) # Order_Total
print(to_valid_identifier("123 Count")) # _123_Count
print(to_valid_identifier("class")) # class_
Output:
Order_Total
_123_Count
class_
The following production script implements an identifier sanitizer and validator with automated self-checking unit assertions:
import re
import keyword
class IdentifierManager:
@staticmethod
def is_valid_variable(name):
# Checks both lexical syntax and keyword availability
if not isinstance(name, str):
return False
return name.isidentifier() and not keyword.iskeyword(name)
@staticmethod
def sanitize(raw_name):
# Normalizes arbitrary string into clean, valid identifier
if not isinstance(raw_name, str):
return "_var"
# Replace non-word characters with underscore
clean = re.sub(r'[^a-zA-Z0-9_]+', '_', raw_name).strip('_')
if not clean:
clean = "_var"
elif clean[0].isdigit():
clean = f"_{clean}"
if keyword.iskeyword(clean):
clean = f"{clean}_"
return clean
# --- Unit Assertions and Verification ---
mgr = IdentifierManager()
# 1. Validation tests
assert mgr.is_valid_variable("user_id") is True
assert mgr.is_valid_variable("_private") is True
assert mgr.is_valid_variable("class") is False # Keyword forbidden
assert mgr.is_valid_variable("2nd_place") is False # Digit prefix forbidden
assert mgr.is_valid_variable("user-name") is False # Hyphen forbidden
assert mgr.is_valid_variable("") is False # Empty string
# 2. Sanitization tests
assert mgr.sanitize("user-name") == "user_name"
assert mgr.sanitize("100_percent") == "_100_percent"
assert mgr.sanitize("def") == "def_"
assert mgr.sanitize("Order Amount ($)") == "Order_Amount"
assert mgr.is_valid_variable(mgr.sanitize("100_percent")) is True
print("Sanitized 'Order Amount ($)':", mgr.sanitize("Order Amount ($)"))
print("Sanitized 'def': ", mgr.sanitize("def"))
print("All str.isidentifier() assertions passed successfully!")
Open in Google Colab View on GitHub
Run all identifier syntax checks interactively, evaluate keyword traps, test Unicode variable names, and sanitize database columns into clean Python variables in Google Colab.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.