A name like total_sales or Customer is what Python calls an identifier: the label you give a variable, function, class or module. It has to start with a letter or underscore, use only letters, digits and underscores after that, and it can’t be a keyword.
To test a string against those rules, call isidentifier():
print("total_sales".isidentifier()) # True
print("2026_revenue".isidentifier()) # False
In this tutorial, I’ll cover the rules for identifiers in Python, the keyword trap that isidentifier() misses, case sensitivity, and a helper that turns messy text into a valid name. Everything here ran on Python 3.12.5.
What is an identifier in Python?
An identifier is any name you bind to an object. When you write tax_rate = 0.0825, tax_rate is the identifier and the float is the object it points to. Our guide to Python variables covers that binding in more depth.
Function names, class names, module names, parameters and attributes are identifiers too. The same rules apply to all of them.
Rules for identifiers in Python
Python’s lexical rules boil down to five points:
- The first character is a letter (
a-z,A-Z, or a Unicode letter) or an underscore_. - The remaining characters are letters, digits
0-9or underscores. No spaces, hyphens,$,@or.. - It can’t be one of the reserved keywords such as
class,iforNone. - Case matters:
totalandTotalare two different names. - There’s no length limit, though PEP 8 suggests keeping lines readable.
Notice that rule 2 is close to a plain character test. If you only need that part, see how to check for only alphanumeric characters and underscores with a regex.
How to check if a string is a valid identifier with isidentifier()
Here’s a sample I’ll reuse through the post: eight column headers from a store’s sales export, saved as columns.py:
# Column headers from a Denver store's 2026 sales export
headers = ["total_sales", "_tax_rate", "2026_revenue", "order-id",
"first name", "café_total", "class", "Price$"]
Now let’s loop over them and call str.isidentifier() on each one:
from columns import headers
for name in headers:
print(f"{name!r:16} isidentifier: {name.isidentifier()}")
Output:
'total_sales' isidentifier: True
'_tax_rate' isidentifier: True
'2026_revenue' isidentifier: False
'order-id' isidentifier: False
'first name' isidentifier: False
'café_total' isidentifier: True
'class' isidentifier: True
'Price$' isidentifier: False

The leading digit, the hyphen, the space and the $ all fail. café_total passes because é is a Unicode letter.
Important: "class".isidentifier() returns True. The method checks the shape of the name only; it doesn’t know about keywords.
What happens when an identifier starts with a digit?
Try to use one of the failing headers as a real variable name and you get an error before a single line runs:
2026_revenue = 48250.75
print(2026_revenue)
Output:
File "C:\pyguides\digit_name.py", line 1
2026_revenue = 48250.75
^
SyntaxError: invalid decimal literal

The parser reads 2026 as the start of a number, so the error mentions a decimal literal rather than a bad name. A stray curly quote or emoji gives a different message, covered in SyntaxError: invalid character in identifier.
The fix is to move the digit: revenue_2026 works fine.
Which Python keywords can’t be used as identifiers?
Keywords are names the language reserves for its own syntax. The keyword module lists them, and iskeyword() tests one:
import keyword
print(len(keyword.kwlist), "keywords")
print(keyword.kwlist[:10])
print("soft:", keyword.softkwlist)
for word in ["class", "Class", "match", "price"]:
print(f"{word!r:8} iskeyword: {keyword.iskeyword(word)}")
Output:
35 keywords
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class']
soft: ['_', 'case', 'match', 'type']
'class' iskeyword: True
'Class' iskeyword: False
'match' iskeyword: False
'price' iskeyword: False

There are 35 hard keywords, including True, False and None. Soft keywords (_, case, match, type) only act as keywords inside certain statements, so match = 5 is still legal.
Assigning to a hard keyword such as class = "Gold" is a SyntaxError. See invalid syntax in Python for how those messages read.
How to check a valid identifier and rule out keywords
Combine both checks and you get a real “can I use this as a name?” test. Same eight headers:
from columns import headers
import keyword
def is_valid_name(text):
return text.isidentifier() and not keyword.iskeyword(text)
for name in headers:
print(f"{name!r:16} valid: {is_valid_name(name)}")
Output:
'total_sales' valid: True
'_tax_rate' valid: True
'2026_revenue' valid: False
'order-id' valid: False
'first name' valid: False
'café_total' valid: True
'class' valid: False
'Price$' valid: False
Now class correctly fails alongside the four malformed headers. That’s the version I use when I generate code or validate field names from user input.
Is Python case sensitive when dealing with identifiers?
Yes. Python compares identifiers character by character, so changing case creates a new name:
total = 120.50
Total = 89.99
TOTAL = 15.00
print(total, Total, TOTAL)
print(total == Total)
Output:
120.5 89.99 15.0
False
Three separate variables hold three different prices, and total == Total is False. A typo in case usually shows up as a NameError later.
The same goes for keywords: True is reserved, true isn’t. If a name is read before it’s assigned inside a function, you’ll hit UnboundLocalError instead.
How to convert a string to a valid identifier in Python
When headers come from a CSV or a form, you can’t rename them by hand. This helper replaces every non-word character with an underscore, prefixes a leading digit, and appends _ to keywords:
from columns import headers
import keyword
import re
def to_identifier(text):
name = re.sub(r"\W", "_", text.strip())
if name[:1].isdigit():
name = "_" + name
if keyword.iskeyword(name):
name += "_"
return name
for name in headers:
print(f"{name!r:16} -> {to_identifier(name)}")
Output:
'total_sales' -> total_sales
'_tax_rate' -> _tax_rate
'2026_revenue' -> _2026_revenue
'order-id' -> order_id
'first name' -> first_name
'café_total' -> café_total
'class' -> class_
'Price$' -> Price_

\W matches anything that isn’t a letter, digit or underscore, and it’s Unicode-aware, so café_total stays as is. The trailing underscore for keywords follows the PEP 8 convention (class_).
Naming conventions for Python identifiers
The rules say what’s legal; conventions say what other Python developers expect to read:
| Identifier | Convention | Example |
|---|---|---|
| Variable, function | snake_case | total_sales, get_tax() |
| Class | CapWords | SalesReport |
| Constant | UPPER_CASE | MAX_ORDERS = 500 |
| Internal use | Leading underscore | _cache |
| Name mangling | Two leading underscores | __balance |
| Special method | Double underscores both sides | __init__ |
For the full style guide, read Python naming conventions, and for module-level values that shouldn’t change, see constants in Python.
Common mistakes with Python identifiers
The one that bites most beginners is reusing a built-in name. It’s legal, which is why it’s dangerous:
list = ["Emily", "Ryan"]
letters = list("abc")
print(letters)
Output:
Traceback (most recent call last):
File "C:\pyguides\runs\isidentifier\ex_shadow.py", line 2, in <module>
letters = list("abc")
^^^^^^^^^^^
TypeError: 'list' object is not callable
Once list points to your own list, the built-in list() is hidden, so calling it raises TypeError: 'list' object is not callable. Use names or customers instead.
- Single letters:
l,OandIlook like 1 and 0 in many fonts. - Hyphens:
order-idis read asorder - id, a subtraction. - Unicode look-alikes: Python normalizes names with NFKC, so a ligature like
filebecomesfile. Stick to ASCII for shared code.
If you’re tidying up names and text in Python, these pair well with this guide:
- Python naming conventions for variables
- Define a function in Python
- Check if a string is ASCII in Python
- Types of errors in Python
Frequently asked questions
What is an identifier in Python?
It’s the name of