You're staring at a multiple-choice question. The question reads: "Which of the following is not a keyword in Python?Maybe you're just curious. Even so, maybe it's a certification exam. Maybe it's a coding interview. " And you freeze.
Because print looks like a keyword. So does input. And range. And len.
Spoiler: none of those are keywords.
Let's clear this up once and for all.
What Are Python Keywords
Keywords are reserved words. The language itself claims them. You can't use them as variable names, function names, class names, or any other identifier. Try naming a variable for and Python will slap you with a SyntaxError before your code even runs.
There are 35 keywords in Python 3.On top of that, 12. Which means that's it. Thirty-five words that belong exclusively to the language.
The Difference Between Keywords and Built-ins
This is where almost everyone gets tripped up Most people skip this — try not to..
print is a built-in function. Practically speaking, len is a built-in function. Still, range is a built-in class. input is a built-in function. str, int, list, dict, set, tuple — all built-in types Most people skip this — try not to..
You can overwrite these. Not that you should. But you can.
print = "hello" # Legal. Terrible idea. But legal.
len = 42 # Also legal. Also terrible.
Try that with if or while or def. You'll get a syntax error instantly. On top of that, that's the difference. Keywords are structural. Built-ins are just... available by default Less friction, more output..
Soft Keywords: The Newer Category
Python 3.10 introduced match and case for structural pattern matching. Even so, they're technically "soft keywords" — recognized in specific contexts but allowed as identifiers elsewhere. Practically speaking, python 3. 12 added type as a soft keyword for the new type statement.
You can still name a variable match outside a match statement. But don't. It's confusing.
Why Keywords Matter
You might wonder why this distinction even matters. Isn't it just trivia?
Not really.
Code That Won't Run
If you try to use a keyword as an identifier, your code fails at parse time. Not runtime. And parse time. Before a single line executes.
class = "my class" # SyntaxError: invalid syntax
def = 5 # SyntaxError: invalid syntax
This catches typos early. Accidentally type retrun instead of return? Python won't stop you. But syntaxError. That's not a keyword — it's just a name. But type return outside a function? The keyword enforces structure.
Reading Other People's Code
When you see yield, you know a generator is involved. Practically speaking, when you see await, you know async code. On the flip side, keywords signal intent. Which means when you see nonlocal, you know nested scopes. They're signposts.
Avoiding Shadowing Bugs
Overwriting built-ins doesn't crash your program. It creates subtle bugs.
list = [1, 2, 3]
new_list = list("abc") # TypeError: 'list' object is not callable
You just broke list() for the rest of that scope. Consider this: this happens in real codebases. Still, knowing what's a keyword vs. a built-in vs. a standard library name saves debugging hours.
The Complete List of Python Keywords
Here they are. In practice, all 35. Grouped by what they do Small thing, real impact..
Control Flow
if, elif, else — conditional branching
for, while — loops
break, continue — loop control
try, except, finally, else — exception handling (yes, else works on loops and try blocks too)
raise — throw an exception
assert — debugging checks
match, case — pattern matching (soft keywords)
Functions and Structure
def — define a function
return — exit a function with a value
yield — pause a generator
lambda — anonymous function
global, nonlocal — scope declarations
pass — placeholder statement
Classes
class — define a class
self is not a keyword. Here's the thing — it's convention. On top of that, you could use this or me or potato. But don't.
Imports and Modules
import, from, as — module handling
Boolean and None
True, False, None — singletons. Capitalized. Not true, false, null.
Logical Operators
and, or, not — boolean logic. Not &&, ||, !.
Membership and Identity
in, is — operators that happen to be keywords
Context Managers
with — resource management
as — also used here for aliasing the context manager result
Async
async, await — asynchronous programming
Other
del — delete references
type — soft keyword for type statements (3.12+)
That's the full list. Bookmark it. Or just run:
import keyword
print(keyword.kwlist)
Common Words That Look Like Keywords But Aren't
This is the section that answers your actual question. These words appear constantly in Python code. None are keywords That alone is useful..
Built-in Functions
print, len, range, input, open, sorted, reversed, enumerate, zip, map, filter, sum, max, min, abs, round, pow, divmod, isinstance, issubclass, hasattr, getattr, setattr, delattr, callable, iter, next, id, hash, help, vars, dir, locals, globals, eval, exec, compile, format, ascii, repr, chr, ord, hex, oct, bin, int, float, str, bool, list, tuple, set, frozenset, dict, bytes, bytearray, memoryview, object, type, super, property, classmethod, staticmethod, slice, complex
Wait — type appears in both lists now. Practically speaking, since 3. 12 it's a soft keyword and a built-in. Context determines which wins.
Built-in Exceptions
Exception, Error, ValueError, TypeError, KeyError, IndexError, AttributeError, ImportError, ModuleNotFoundError, FileNotFoundError, PermissionError, OSError, RuntimeError, StopIteration, StopAsyncIteration, KeyboardInterrupt, SystemExit, GeneratorExit, BaseException
Built-in Constants
Ellipsis, NotImplemented, __debug__
Built‑in Exceptions – the safety net of every Python program
When you deliberately raise an error or let an operation bubble up, you are usually working with one of the exception classes that live in the builtins module. They are not keywords, but they behave like reserved identifiers because they appear in almost every traceback you’ll ever read That alone is useful..
| Category | Typical subclasses | When you’ll see them |
|---|---|---|
| Base | BaseException |
The root of everything that can be caught with except. And |
| System‑level | SystemExit, KeyboardInterrupt, GeneratorExit |
Triggered by sys. Worth adding: exit(), Ctrl‑C, or generator clean‑up. On the flip side, |
| Standard errors | Exception, RuntimeError, SyntaxError, TypeError, ValueError |
The workhorses for ordinary error handling. That's why |
| Data‑structure errors | KeyError, IndexError, AttributeError, TypeError (again) |
Occur when you try to access a missing key, out‑of‑range index, or an attribute that doesn’t exist. |
| Import failures | ImportError, ModuleNotFoundError, ImportError (pre‑3.12) |
Happens when Python cannot locate a module or package. Worth adding: |
| IO problems | FileNotFoundError, PermissionError, OSError |
Raised during file reads/writes or when the underlying OS reports an issue. |
| Network / concurrency | TimeoutError, ConnectionError, BrokenPipeError |
Specific to sockets, HTTP requests, or other external resources. |
This changes depending on context. Keep that in mind.
You can subclass any of these to create domain‑specific error types, e.g.:
class ValidationError(ValueError):
"""Raised when user input does not meet validation rules."""
pass
Because they are ordinary classes, they can be caught, inspected, or re‑raised just like any other object.
Built‑in Constants – the immutable building blocks
Python ships with a handful of singleton objects that are globally available without any import. They are written with an initial capital letter, just like the boolean singletons The details matter here..
Ellipsis– represented by the literal.... It is used in slicing syntax and can also serve as a placeholder in custom indexing schemes.NotImplemented– a signal to the interpreter that a method does not yet have an implementation; it should be overridden by a subclass.__debug__– evaluates toFalsewhen the interpreter is started with the-O(optimize) flag, allowing you to strip away debugging code in production builds.
These constants are immutable, hashable, and can be compared directly (if x is NotImplemented:), but they are not keywords; they are simply objects defined in the standard library.
Using Soft Keywords Wisely
Since Python 3.12, the parser treats certain identifiers as soft keywords only inside specific syntactic contexts (e.Even so, g. Also, , type in type‑annotation statements). Outside those contexts they behave exactly like regular identifiers, which means you can safely use them as variable names or function arguments when you are not annotating types Most people skip this — try not to..
def compute_type(x) -> None: # 'type' is just a normal parameter name here
pass
When you write a type hint, however, the parser switches modes:
def compute_type(x: int) -> int: # now 'type' is a soft keyword, not allowed as a plain identifier
...
If you need a variable called type in a context where a type hint is expected, you can work around it by using a different name or by employing an annotation that does not involve the soft keyword.
Common Pitfalls and How to Avoid Them
-
Accidentally shadowing a built‑in – Renaming a variable to
list,dict, orstrworks, but it obscures the original function and can lead to subtle bugs.
Tip: Keep built‑ins untouched; if you must use a similar name, limit its scope to a tiny inner block Most people skip this — try not to.. -
Misreading soft keywords
-
Misreading soft keywords – Because identifiers like
match,case, ortypeare only treated as keywords in specific syntactic positions, it’s easy to assume they are reserved everywhere and avoid them unnecessarily, or conversely to use them where they actually permitted.
**Check the language reference the the identifier is a soft keyword only when it appears after a colon in a type annotation, inside amatchstatement, or followingcase. In all other places you can safely name variables, functions, or classes with those identifiers Not complicated — just consistent. Nothing fancy..- Example:
def case_insensitive_sort(seq): …is fine becausecaseis not in a type‑annotation or pattern‑matching context. - Tip: When in doubt, run
python -m aston a snippet; the AST will show whether the token was parsed as aNameor as aKeyword.
- Example:
-
Over‑relying on
Ellipsisas a “no‑op” placeholder – While...is convenient for stubbing out functions (def foo(): ...) or marking slices (a[..., 0]), using it in production logic can be confusing because it evaluates to a singleton object that is truthy (bool(...) is True). If you later forget to replace the ellipsis with real code, the function will still return a value (Noneis implicit, but the ellipsis itself may be inspected elsewhere).- Tip: Reserve
...for genuine stubbing during development, and replace it with apassor araise NotImplementedErrorbefore merging to main. - Alternative: Use a custom sentinel object (
_UNIMPLEMENTED = object()) and checkif result is _UNIMPLEMENTED:to make the intent explicit.
- Tip: Reserve
-
Assuming
NotImplementedsignals an error – ReturningNotImplementedfrom a rich‑comparison method (__eq__,__lt__, etc.) tells Python to try the reflected operation on the other operand; it is not an exception. Mistakenly treating it as a failure and raising an error yourself can break fallback semantics and lead to surprisingTypeErrors.- Tip: Only return
NotImplementedwhen you genuinely cannot compare with the supplied type; otherwise returnFalseorTrueas appropriate. - Example:
class Money: def __eq__(self, other): if not isinstance(other, Money): return NotImplemented # let other.__eq__ try return self.amount == other.amount and self.currency == other.currency - Tip: Only return
-
Neglecting the effect of
__debug__on assertions – Sinceassertstatements are removed when__debug__isFalse(i.e., when running with-O), code that relies on assertions for runtime checks will silently skip those checks in optimized builds.- Tip: Use assertions only for conditions that should never happen in correct code (pre‑conditions, post‑conditions, invariants). For validation that must run in production, raise explicit exceptions instead.
- Example of misuse:
def withdraw(account, amount): assert amount > 0, "amount must be positive" # disappears with -O ...Better:
if amount <= 0: raise ValueError("amount must be positive")
Putting It All Together
Understanding the nuances of Python’s built‑in objects, the contextual nature of soft keywords, and the subtle behaviors of constants like Ellipsis, NotImplemented, and __debug__ empowers you to write code that is both expressive and solid. By reserving built‑in names for their intended purposes, using soft keywords only where they are syntactically meaningful, and treating placeholders and sentinel objects with clear intent, you avoid the most common sources of confusion and bugs Less friction, more output..
Conclusion
Python’s rich set of built‑ins and its evolving syntax give developers powerful tools, but they also demand careful attention to scope and context. By keeping built‑ins untouched, respecting the conditional nature of soft keywords, and using constants like Ellipsis, NotImplemented, and __debug__ exactly as they were designed, you harness the language’s strengths while minimizing surprises. Apply these practices consistently, and your code will remain readable, maintainable, and resilient across development and production environments Practical, not theoretical..