Python keyword: Reserved Words

Published on: August 7, 2026
Reading time: 5 minutes
Source code representing reserved words and soft keywords in Python

Code generators, form validators, refactoring tools, and systems that transform external names into Python identifiers need to know whether a word can be used as a variable, attribute, function, or class. The Python keyword module exposes the reserved words recognized by the current interpreter and the soft keywords that have special meaning only in selected grammatical contexts.

This guide covers iskeyword(), kwlist, issoftkeyword(), and softkwlist, including version-aware identifier validation. It complements our articles about tokenize, symtable, codeop, Python AST, and types.

What a reserved word is

Reserved words participate in Python’s grammar and cannot be ordinary identifiers.

class = "report"  # SyntaxError
return = 10       # SyntaxError

Familiar examples include class, def, return, if, for, while, try, import, and lambda. The list depends on the Python version and should not be hard-coded in long-lived tooling.

Check with iskeyword()

keyword.iskeyword() reports whether a string is a reserved word for the current interpreter.

import keyword

print(keyword.iskeyword("class"))    # True
print(keyword.iskeyword("customer")) # False

The function checks reserved-word status only. It does not validate every identifier rule.

Inspect kwlist

keyword.kwlist contains reserved words in alphabetical order.

for word in keyword.kwlist:
    print(word)

The list supports educational interfaces, syntax highlighting, tests, and alternative-name generation.

Do not mutate kwlist

Although the exposed object is a list, changing it does not alter Python’s grammar and can break code that relies on the module.

# Do not do this
keyword.kwlist.append("customer")

Treat it as read-only information. Copy it or build a frozenset for application use.

RESERVED = frozenset(keyword.kwlist)

What soft keywords are

Soft keywords receive special meaning only in certain grammatical contexts. Outside those contexts, they may remain valid identifiers.

This mechanism lets Python evolve while reducing breakage of existing programs. Modern features such as pattern matching and type declarations use contextual words.

Check soft keywords

print(keyword.issoftkeyword("match"))
print(keyword.issoftkeyword("case"))
print(keyword.issoftkeyword("customer"))

The official keyword documentation recommends querying the module rather than maintaining fixed lists because grammar evolves.

Inspect softkwlist

for word in keyword.softkwlist:
    print(word)

Not every soft keyword must be rejected as a name. The decision depends on where the identifier will appear.

A soft keyword may still be an identifier

match = "text"
case = 10
print(match, case)

In structural pattern matching, the same words participate in syntax.

match value:
    case 0:
        print("zero")

A code generator must consider the complete context instead of blocking every soft keyword unconditionally.

Validate a complete identifier

Combine str.isidentifier() with iskeyword().

def valid_identifier(name: str) -> bool:
    return name.isidentifier() and not keyword.iskeyword(name)

print(valid_identifier("customer_id"))
print(valid_identifier("2customers"))
print(valid_identifier("class"))

isidentifier() follows the Unicode identifier rules of the current release. It does not determine whether a name is readable or allowed by project policy.

Unicode names

Python accepts many Unicode characters in identifiers.

print("ação".isidentifier())
print("π".isidentifier())

The official lexical analysis reference describes normalization and permitted categories. Public APIs may prefer ASCII for interoperability, but that is a project policy rather than a language requirement.

Generate a safe identifier

import re
import unicodedata


def make_identifier(text: str) -> str:
    text = unicodedata.normalize("NFKC", text).strip()
    text = re.sub(r"\W+", "_", text, flags=re.UNICODE)
    text = text.strip("_") or "value"

    if text[0].isdigit():
        text = "_" + text

    if keyword.iskeyword(text):
        text += "_"

    if not text.isidentifier():
        raise ValueError("could not create identifier")

    return text

Do not use normalization alone for authorization decisions. Visually similar Unicode characters can still differ.

The trailing-underscore convention

class_ = "Report"
from_ = "source"
async_ = False

A trailing underscore is the conventional escape for reserved words. Keep a mapping between original external field names and Python identifiers.

Attribute names

Reserved words can be dictionary keys and can even be stored as dynamic attributes.

obj.__dict__["class"] = "special"
print(getattr(obj, "class"))

However, obj.class is invalid syntax. Code generators must reject or transform such names even when setattr() accepts them.

JSON and database fields

External schemas may legitimately contain class, from, or global. Do not silently alter the external schema.

FIELD_MAP = {
    "class": "class_",
    "from": "from_",
}

value = payload["class"]
model.class_ = value

Serialization back to the external format should restore the original name.

Generate function parameters

def safe_signature(names):
    for name in names:
        if not valid_identifier(name):
            raise ValueError(f"invalid parameter: {name!r}")
    return ", ".join(names)

Even with valid identifiers, concatenating and executing code remains dangerous when other parts come from users. Prefer closures, data structures, or restricted AST construction.

Keywords in templates

A template that generates classes, dataclasses, or APIs should validate class names, attributes, parameters, and methods separately.

A name accepted by getattr() is not necessarily valid after a dot in source code. Compile representative output in tests.

Compatibility across Python versions

A word may become reserved or contextual in a future release. Tools producing code for another runtime should not rely only on the interpreter running the generator.

Options include:

  • validating with the target interpreter;
  • maintaining versioned tables from official sources;
  • compiling generated code under every supported release;
  • using containers or a CI Python matrix.

Do not reuse one version’s list forever

Persisting an old kwlist indefinitely may permit syntax that is invalid in newer releases or reject words that changed status.

Record the interpreter version alongside generated artifacts and test minimum and maximum supported versions.

Syntax highlighting

Editors can use kwlist for hard keywords and softkwlist for contextual highlighting. Coloring every soft keyword everywhere creates false positives.

Accurate highlighting requires a tokenizer or parser. The keyword module provides classification, not grammatical position.

Integration with tokenize

tokenize commonly reports both identifiers and keywords as NAME tokens. The parser makes the final distinction.

import io
import tokenize

source = b"if value: pass\n"
for token in tokenize.tokenize(io.BytesIO(source).readline):
    if token.type == tokenize.NAME:
        print(token.string, keyword.iskeyword(token.string))

This supports lightweight analyzers, while soft keywords still need context.

Integration with AST

The parser applies the actual grammar of the current version. ast.parse() or compilation is the strongest confirmation that a complete source fragment is syntactically valid.

import ast

try:
    ast.parse("customer = 1")
except SyntaxError as error:
    print(error)

Parsing validates syntax without executing the source.

Refactoring tools

When renaming symbols, check:

  • whether the new name is an identifier;
  • whether it is a hard keyword;
  • whether it conflicts in the scope;
  • whether a soft keyword is valid in that context;
  • whether strings and configuration references also need updates.

symtable and AST help analyze scopes and references.

API validation

def validate_field(name):
    if not name.isidentifier():
        raise ValueError("field is not a Python identifier")
    if keyword.iskeyword(name):
        raise ValueError(f"{name!r} is reserved")

An API can offer an alternative automatically, but should clearly expose the mapping.

Performance

Module checks are fast. Tools validating millions of values can build immutable sets.

KEYWORDS = frozenset(keyword.kwlist)
SOFT_KEYWORDS = frozenset(keyword.softkwlist)

Rebuild these sets whenever the interpreter changes.

Testing

def test_identifiers():
    assert valid_identifier("customer")
    assert valid_identifier("ação")
    assert not valid_identifier("class")
    assert not valid_identifier("2customers")
    assert not valid_identifier("customer-id")

Include every current keyword, contextual words in several positions, Unicode, empty strings, and names close to built-ins.

Built-ins are not keywords

Names such as list, dict, str, id, and input are not reserved.

print(keyword.iskeyword("list"))  # False

Assigning to them is legal but shadows useful built-ins.

list = [1, 2, 3]
# list("abc") now fails

Linters can enforce this additional policy.

Common mistakes

  • Validating only with iskeyword().
  • Rejecting every soft keyword in every context.
  • Maintaining a stale manual list.
  • Assuming setattr() guarantees valid dot syntax.
  • Confusing built-ins with reserved words.
  • Generating for another version without testing it.
  • Mutating kwlist or softkwlist.
  • Executing code merely to validate syntax.

Best practices

  • Combine isidentifier() and iskeyword().
  • Evaluate soft keywords in context.
  • Query or test the actual target runtime.
  • Keep mappings for transformed external names.
  • Use AST or compilation for complete fragments.
  • Test all supported Python versions.
  • Lint shadowed built-ins.
  • Handle Unicode normalization deliberately.

Conclusion

The Python keyword module provides the official hard and contextual keyword lists for the running interpreter. iskeyword() and issoftkeyword() help validators, generators, editors, and refactoring tools follow language evolution.

Correct validation goes beyond one list: names must satisfy isidentifier(), soft keywords depend on context, and source intended for another version must be tested there. These layers let tools produce readable identifiers without generating invalid syntax.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Software architecture representing abstract base classes with Python abc
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python abc: Abstract Base Classes

    Learn Python abc to create abstract classes, required methods, virtual subclasses, and stable runtime contracts.

    Ler mais

    Tempo de leitura: 5 minutos
    06/08/2026
    Code and structures representing runtime types with the Python types module
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python types: Runtime Type Utilities

    Learn Python types for SimpleNamespace, MappingProxyType, runtime type names, and safe dynamic class creation.

    Ler mais

    Tempo de leitura: 5 minutos
    06/08/2026
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python sched: Schedule Events

    Learn Python sched to schedule events, control priorities, cancel tasks, and build recurring work with a monotonic clock.

    Ler mais

    Tempo de leitura: 7 minutos
    05/08/2026
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python atexit: Run Cleanup on Exit

    Learn Python atexit to run cleanup at shutdown, control LIFO order, and avoid problems with threads, signals, and handler exceptions.

    Ler mais

    Tempo de leitura: 6 minutos
    05/08/2026
    Monitor with binary code representing pickle opcode analysis with Python pickletools
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python pickletools: Analyze Pickles

    Learn Python pickletools to disassemble pickles, inspect opcodes, and optimize streams without executing untrusted data.

    Ler mais

    Tempo de leitura: 6 minutos
    05/08/2026
    Source code on screen representing analysis with Python tokenize
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python tokenize: Analyze Source Code

    Learn Python tokenize to inspect tokens, comments, indentation, encodings, source positions, and rebuild code safely.

    Ler mais

    Tempo de leitura: 6 minutos
    05/08/2026