Python Keywords Reference
Python keywords are the reserved words that form the grammar of the language itself — things like if, for, class, and return. They are baked into the interpreter’s parser, which means you cannot use them as variable, function, or class names. Understanding the full list of keywords, what each does, and how they differ from ordinary built-in names like print or len is essential for writing correct Python and for understanding error messages when you accidentally collide with one.
Overview: What Are Keywords?
A keyword is a word that Python’s tokenizer recognizes as part of the language’s syntax rather than as an identifier you get to define. When the interpreter reads your source code, it breaks it into tokens, and any token that matches a keyword is treated specially by the grammar — it triggers a statement or expression form (a loop, a conditional, a function definition, and so on) instead of being looked up as a variable. Because of this, trying to write class = 5 or def = \"oops\" raises a SyntaxError before your program even runs.
As of Python 3.12 there are 35 keywords. They fall into a few natural categories:
- Constant values:
True,False,None - Boolean and comparison operators:
and,or,not,in,is - Control flow:
if,elif,else,for,while,break,continue,pass - Functions and classes:
def,return,lambda,class,yield - Exception handling:
try,except,finally,raise,assert - Imports and namespaces:
import,from,as,global,nonlocal,del - Context managers:
with - Asynchronous programming:
async,await
Python also has a small set of soft keywords: match, case, _, and (since 3.12) type. These behave as keywords only in the specific syntactic position where they introduce special syntax (like the start of a match statement); everywhere else they remain ordinary, reusable identifiers. This is different from the 35 hard keywords above, which are reserved everywhere, unconditionally.
It’s also worth distinguishing keywords from built-in names such as print, len, list, or Exception. Those are not keywords at all — they are ordinary names that live in the built-in namespace and are looked up like any variable. That means you technically can write list = [1, 2, 3], shadowing the built-in list type in that scope, whereas you can never do that with a true keyword like class. Shadowing built-ins is legal but almost always a bad idea (see Common Mistakes below).
Syntax
Keywords don’t have a single \”syntax\” of their own since each one participates in a different statement or expression form, but Python gives you a programmatic way to inspect the current set of keywords via the standard library’s keyword module:
import keyword
keyword.iskeyword(name) # True if `name` is a hard keyword
keyword.issoftkeyword(name) # True if `name` is a soft keyword
keyword.kwlist # list of all hard keywords
keyword.softkwlist # list of all soft keywords
| Function / attribute | Returns |
|---|---|
keyword.kwlist |
A list of all 35 hard keyword strings |
keyword.softkwlist |
A list of soft keywords: _, case, match, type |
keyword.iskeyword(s) |
True/False — is s a hard keyword? |
keyword.issoftkeyword(s) |
True/False — is s a soft keyword? |
Examples
Example 1: Inspecting keywords programmatically
import keyword
print(keyword.kwlist)
print(len(keyword.kwlist))
print(keyword.iskeyword(\"class\"))
print(keyword.iskeyword(\"variable\"))
print(keyword.softkwlist)
Output:
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
35
True
False
['_', 'case', 'match', 'type']
This is the most reliable way to check whether a name is reserved — rather than memorizing the list, you can ask the interpreter directly. Notice that keyword.iskeyword(\"variable\") returns False: ordinary identifiers, including built-in function names, are never in kwlist.
Example 2: Keywords from several categories working together
def classify_number(n: int) -> str:
if n < 0:
return \"negative\"
elif n == 0:
return \"zero\"
else:
return \"positive\"
def safe_divide(a: float, b: float) -> float | None:
try:
result = a / b
except ZeroDivisionError:
print(\"Cannot divide by zero\")
return None
else:
return result
finally:
print(\"Division attempt finished\")
def even_numbers(limit: int):
for i in range(limit):
if i % 2 == 0:
yield i
for label in (-3, 0, 7):
print(label, \"->\", classify_number(label))
print(safe_divide(10, 2))
print(safe_divide(10, 0))
print(list(even_numbers(10)))
is_ready = True
has_error = False
if is_ready and not has_error:
print(\"Proceeding\")
Output:
-3 -> negative
0 -> zero
7 -> positive
Division attempt finished
5.0
Cannot divide by zero
Division attempt finished
None
[0, 2, 4, 6, 8]
Proceeding
This one program touches control-flow keywords (if/elif/else, for), function keywords (def, return, yield), exception-handling keywords (try/except/else/finally), and boolean operators (and, not). Notice that finally always runs before the function actually returns — that’s why \”Division attempt finished\” prints before the returned value is printed by the caller.
Example 3: Soft keywords are still ordinary names
def describe(value):
match value:
case 0:
return \"zero\"
case int() | float() if value < 0:
return \"negative number\"
case [first, *rest]:
return f\"list starting with {first}, {len(rest)} more\"
case {\"name\": name}:
return f\"dict with name={name}\"
case _:
return \"something else\"
match = \"I am a variable, not a keyword\"
case = 42
type = str
print(describe(0))
print(describe(-5))
print(describe([1, 2, 3]))
print(describe({\"name\": \"Ada\"}))
print(describe(\"hello\"))
print(match)
print(case)
print(type)
Output:
zero
negative number
list starting with 1, 2 more
dict with name=Ada
something else
I am a variable, not a keyword
42
Inside the describe function, match and case introduce pattern-matching syntax. But at module level, this example freely reassigns match, case, and even type (normally a built-in) to plain variables with no error. That's the defining trait of a soft keyword: it's only special in the exact grammatical position where the parser expects it.
Under the Hood: How Python Recognizes Keywords
When CPython compiles a source file, it first runs a tokenizer that splits the raw text into tokens (identifiers, literals, operators, punctuation). Any identifier-looking token that exactly matches one of the 35 entries in keyword.kwlist is emitted as a distinct token type rather than a generic NAME token. The grammar rules that the parser applies afterward reference these keyword tokens directly — for example, the rule for a while loop literally requires the while keyword token followed by an expression, a colon, and an indented block. Because the check happens at the tokenizer level, a name that matches a keyword can never reach the later stage where identifiers are bound to values; the parser rejects it as a syntax error immediately.
Soft keywords work differently. They are ordinary NAME tokens all the way through tokenization. The parser (Python's PEG parser, in use since 3.9) only treats them specially when it is trying to match a specific grammar rule, such as the start of a match statement, and backtracks to treat them as regular names otherwise. This is why match = \"I am a variable, not a keyword\" compiles fine even though match is meaningful syntax elsewhere in the same file.
Built-in names like print, len, and Exception live one layer further out still: they are just entries in the builtins module, found via normal name lookup (local → enclosing → global → builtins). The interpreter never gives them special tokenizer treatment, which is exactly why they can be shadowed.
Common Mistakes
Mistake 1: Trying to use a keyword as an identifier
Beginners often try something like this, not realizing class is reserved:
class = \"Algebra 101\" # SyntaxError: invalid syntax
This raises SyntaxError: invalid syntax at compile time, before any code runs. Fix it by picking a name that isn't reserved, such as class_name or course_name:
class_name = \"Algebra 101\"
print(class_name)
Mistake 2: Confusing built-ins with keywords
Some learners assume names like list, str, or type are protected keywords and are surprised when this compiles without error:
list = [1, 2, 3]
print(list)
# list([4, 5]) would now fail: 'list' object is not callable
This is legal because list is a built-in, not a keyword — but it silently breaks any later code in that scope that needs the real list type or function. Avoid reusing built-in names for your own variables, even though Python allows it.
Mistake 3: Using is instead of ==
The is keyword checks object identity (same object in memory), not equality of value:
a = 1000
b = 1000
print(a == b) # True: same value
print(a is b) # False on most interpreters: different int objects
Use == to compare values. Reserve is for comparisons against singletons like None, True, and False (e.g. if value is None:), where identity is exactly what you want to check.
Best Practices
- Use
keyword.iskeyword()in tooling or dynamic code (e.g. generating variable names from user input) instead of hardcoding the keyword list — it stays correct across Python versions. - Never shadow built-in names (
list,dict,str,id,type,input, and so on) with your own variables, even though it's legal; it causes confusing bugs later in the same scope. - Always compare with
==for equality and reserveisfor identity checks againstNone,True, andFalse. - When you need a trailing-underscore workaround for a name that collides with a keyword (a common convention, e.g.
class_ortype_), keep it consistent across your codebase rather than inventing a new pattern each time. - Remember that soft keywords (
match,case,_,type) are safe to use as ordinary variable names outside their special syntax, but naming a variablematchright next to amatchstatement can still confuse readers — prefer clarity over cleverness. - In a
matchstatement, remember_is the wildcard pattern, not a variable capture; don't rely on its value afterward.
Practice Exercises
Exercise 1
Write a script that loops over the strings \"for\", \"format\", \"class\", and \"classroom\", and for each one prints whether it is a valid Python keyword using the keyword module.
Exercise 2
Write a function safe_name(name: str) -> str that takes a proposed variable name and, if it collides with a Python keyword, returns the name with an underscore appended (e.g. \"class\" → \"class_\"); otherwise it returns the name unchanged. Test it on \"class\", \"lambda\", and \"total\".
Exercise 3
Write a match statement that takes an HTTP status code (an int) and returns \"success\" for codes in the 200s, \"redirect\" for codes in the 300s, \"client error\" for codes in the 400s, and \"unknown\" otherwise. (Hint: you can guard a pattern with if 200 <= code < 300.)
Summary
- Python has 35 hard keywords (as of 3.12), reserved everywhere and unusable as identifiers; use
keyword.kwlistto see the current list. - Soft keywords —
match,case,_, andtype— are only special in specific grammar positions and remain ordinary names elsewhere; check them withkeyword.softkwlist/issoftkeyword(). - Built-in names like
printandlistare not keywords at all; they can technically be shadowed, but doing so is a common source of bugs. - Keywords are recognized at the tokenizer level, which is why misusing one causes an immediate
SyntaxErrorrather than a runtime error. ischecks identity, not equality — use==for comparing values.- When a keyword collides with a name you want, adopt a consistent convention (like a trailing underscore) rather than reaching for built-in shadowing.
