Keywords #
Keywords are words reserved by Python with a special meaning that can’t be changed. You can’t use them as variable, function, or class names. Python 3.12 has 35 keywords — and understanding each keyword deeply, not just memorizing the list, is a sign of solid language mastery. This article covers all Python keywords grouped by function, complete with examples, the right usage context, and the subtle differences that often confuse people.
The Complete List of Python Keywords #
import keyword
print(keyword.kwlist)
['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', 'match', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return',
'try', 'type', 'while', 'with', 'yield']
# Check whether a string is a keyword
import keyword
print(keyword.iskeyword("for")) # → True
print(keyword.iskeyword("forEach")) # → False
print(keyword.iskeyword("type")) # → True (Python 3.12+)
To make all these keywords easier to understand, we can group them into several main categories based on their specific function in Python programming:
flowchart TD
Root["35 Python Keywords"] --> Val["Special Values"]
Root --> Logic["Logical & Memory Operators"]
Root --> Flow["Flow Control & Loops"]
Root --> Def["Definitions & OOP"]
Root --> Err["Error Handling"]
Root --> Scope["Scope & Namespace"]
Root --> Async["Async (Concurrency)"]
Val --> val_kw["True, False, None"]
Logic --> logic_kw["and, or, not, is, in"]
Flow --> flow_kw["if, elif, else, for, while, break, continue, pass, match"]
Def --> def_kw["def, return, yield, class, lambda, type"]
Err --> err_kw["try, except, finally, raise, assert"]
Scope --> scope_kw["global, nonlocal, del, import, from, as, with"]
Async --> async_kw["async, await"]By dividing the keywords into these functional groups, you can see a map of what each keyword is for and how they work together to shape program logic.
Special Values: True, False, None
#
These three keywords represent special values used throughout Python code.
True and False
#
# True and False are instances of bool, a subclass of int
print(type(True)) # → <class 'bool'>
print(type(False)) # → <class 'bool'>
print(isinstance(True, int)) # → True
# Arithmetic consequences
print(True + True) # → 2
print(True * 10) # → 10
print(False + 1) # → 1
# Conversion to bool — truthy and falsy
print(bool(0)) # → False
print(bool("")) # → False
print(bool([])) # → False
print(bool(None)) # → False
print(bool(42)) # → True
print(bool("text")) # → True
# ANTI-PATTERN: explicit comparison with True/False
if active == True: # redundant
pass
if active is True: # be careful — only right for genuine bools
# CORRECT: evaluate directly
if active:
pass
if not active:
pass
None
#
# None is the only value of NoneType
print(type(None)) # → <class 'NoneType'>
print(None == False) # → False
print(None == 0) # → False
print(None is None) # → True ← the correct way
# Common uses of None
def find(data, key):
"""Return None if not found."""
return data.get(key) # dict.get() returns None by default
# A function without an explicit return returns None
def print_only(text):
print(text)
result = print_only("hello")
print(result) # → None
# None as a sentinel default parameter
def add(item, container=None):
if container is None:
container = [] # create a new one each time — avoid mutable defaults
container.append(item)
return container
# ALWAYS compare None with 'is' or 'is not', not == or !=
value = None
if value is None: # ✓ correct
pass
if value is not None: # ✓ correct
pass
if value == None: # ✗ unidiomatic (even though it works)
pass
Logical Operators: and, or, not
#
# and — return the first falsy operand, or the last if all are truthy
print(True and True) # → True
print(True and False) # → False
print(0 and "hello") # → 0 (0 is falsy, stop)
print(1 and "hello") # → "hello" (all truthy, return the last)
# or — return the first truthy operand, or the last if all are falsy
print(False or True) # → True
print(0 or "") # → "" (all falsy, return the last)
print(0 or "default") # → "default"
print("exists" or "default") # → "exists"
# not — boolean negation
print(not True) # → False
print(not False) # → True
print(not 0) # → True
print(not "") # → True
print(not [1, 2]) # → False
# Practical idioms leveraging short-circuit
name = input_name or "Guest" # default value
data and process(data) # conditional execution
result = x if x is not None else 0 # explicit alternative
Identity and Membership Operators: is, in
#
is and is not
#
# is — check whether two variables point to THE SAME OBJECT (not equal values)
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # → True (values are equal)
print(a is b) # → False (different objects in memory)
print(a is c) # → True (c is an alias of a — the same object)
# When is is the right tool:
# 1. Comparing with None
if result is None:
pass
if user is not None:
pass
# 2. Comparing singletons (True, False, None)
# Avoid is for ints, strs, lists, or other types
# because Python caches some small objects unpredictably
x = 256
y = 256
print(x is y) # → True (cached by Python)
x = 257
y = 257
print(x is y) # → possibly False (not always cached)
in and not in
#
# in — check membership in a collection or substring in a string
print("a" in "bahasa") # → True (substring)
print("z" not in "bahasa") # → True
print(3 in [1, 2, 3, 4]) # → True (list — O(n))
print(3 in {1, 2, 3, 4}) # → True (set — O(1))
print("name" in {"name": "Budi"}) # → True (dict — checks the KEY)
# in in a for loop — iteration
for letter in "Python":
print(letter, end=" ")
# → P y t h o n
for i in range(5):
print(i, end=" ")
# → 0 1 2 3 4
Flow Control: if, elif, else
#
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
elif score >= 60:
grade = "D"
else:
grade = "E"
print(grade) # → B
# Conditional expression (ternary) — if/else in one line
status = "passed" if score >= 60 else "failed"
# if in a comprehension
even = [x for x in range(10) if x % 2 == 0]
Loops: for, while, break, continue, pass
#
for and while
#
# for — iterate over an iterable
for item in ["apple", "orange", "mango"]:
print(item)
# while — repeat while the condition is True
count = 0
while count < 5:
print(count)
count += 1
# while True — an infinite loop with break as the exit
while True:
command = input("Command: ")
if command == "exit":
break
execute(command)
break — Stop the Loop
#
# break stops the loop entirely and exits
numbers = [1, 5, 3, 8, 2, 9]
for n in numbers:
if n > 7:
print(f"First > 7: {n}")
break # stop the loop — don't continue to the next element
# → First > 7: 8
# break only exits the INNERMOST loop
for i in range(3):
for j in range(3):
if j == 1:
break # only exits the j loop, the i loop continues
print(f"i={i}") # this still runs
continue — Skip This Iteration
#
# continue jumps to the next iteration without running the rest of the block
for i in range(10):
if i % 2 == 0:
continue # skip even numbers
print(i, end=" ")
# → 1 3 5 7 9
# Useful for early skipping with guard clauses
for user in user_list:
if not user.active:
continue # skip inactive users
if user.balance < 0:
continue # skip users with a negative balance
send_promo(user) # only runs for valid users
pass — Do Nothing
#
# pass is a syntax placeholder — used when a code block is required
# but not yet implemented
class NewModel:
pass # empty class — syntactically valid
def todo_function():
pass # TODO: implement later
# In conditions that are intentionally ignored
for item in data:
if special_condition(item):
pass # intentionally ignored
else:
process(item)
# Unlike ... (Ellipsis), which is also often used as a placeholder
def abstract_function() -> None:
... # Ellipsis — more common in type stubs and ABCs
else on Loops
#
# else on for/while — runs ONLY if the loop finishes WITHOUT break
for n in [2, 4, 6, 8]:
if n % 2 != 0:
print(f"{n} is not even")
break
else:
print("All numbers are even!") # → this is what runs
# → All numbers are even!
# Search example
def is_prime(n):
for divisor in range(2, int(n**0.5) + 1):
if n % divisor == 0:
return False # not prime
return True # prime
Definitions: def, class, lambda, return, yield
#
def and return
#
# def defines a function
def greet(name: str) -> str:
return f"Hello, {name}!"
# return ends the function and returns a value
# Without an explicit return, the function returns None
def no_return():
x = 42 # not returned
# return can return several values (as a tuple)
def stats(data):
return min(data), max(data), sum(data) / len(data)
mn, mx, avg = stats([1, 2, 3, 4, 5])
# return without a value — returns None and stops the function
def validate(data):
if not data:
return # early return — equivalent to return None
process(data)
class
#
# class defines a class (a blueprint for objects)
class Animal:
def __init__(self, name: str):
self.name = name
def make_sound(self) -> str:
raise NotImplementedError
class Dog(Animal): # Dog inherits from Animal
def make_sound(self) -> str:
return "Woof!"
# a class can inherit from several classes (multiple inheritance)
class Amphibian(Animal, Swimmer, Jumper):
pass
lambda
#
# lambda — a one-expression anonymous function
square = lambda x: x ** 2
print(square(5)) # → 25
# Most useful as a function argument
data = [{"name": "Budi", "score": 85}, {"name": "Ani", "score": 92}]
sorted_data = sorted(data, key=lambda d: d["score"], reverse=True)
# ANTI-PATTERN: lambda for complex logic
process = lambda x, y: x**2 + y**2 if x > 0 and y > 0 else 0
# CORRECT: a regular function for non-trivial logic
def process(x, y):
if x > 0 and y > 0:
return x**2 + y**2
return 0
yield — Generator Functions
#
# yield makes a function a generator — producing values one at a time
def countdown(n):
while n > 0:
yield n # "return" n, but don't stop the function
n -= 1
for number in countdown(5):
print(number, end=" ")
# → 5 4 3 2 1
# Generators save memory — values are produced lazily
def read_big_file(path):
with open(path) as f:
for line in f:
yield line.strip() # produce one line at a time
# yield from — delegate to another generator
def merge(*iterables):
for it in iterables:
yield from it # cleaner than: for item in it: yield item
list(merge([1, 2], [3, 4], [5])) # → [1, 2, 3, 4, 5]
Imports: import, from, as
#
# import — import a whole module
import os
import math
import datetime
print(math.pi) # access with the module name
print(os.getcwd())
# from ... import — import specific items from a module
from math import pi, sqrt, floor
from datetime import datetime, timedelta
from pathlib import Path
print(pi) # access directly without the module prefix
print(sqrt(16)) # → 4.0
# as — give an alias to an imported module or item
import numpy as np # alias for long names
import pandas as pd
from datetime import datetime as dt # alias to avoid conflicts
# from ... import * — import everything (avoid this!)
# from math import * # ANTI-PATTERN: unclear what gets imported
# Relative imports (inside a package)
from . import utils # import from a module in the same package
from ..models import User # import from the parent package
Error Handling: try, except, raise, finally, else
#
# Complete exception handling structure
try:
result = int(input("Enter a number: "))
print(10 / result)
except ValueError:
print("Input is not a valid number")
except ZeroDivisionError:
print("Can't divide by zero")
except (TypeError, OverflowError) as e:
print(f"Unexpected error: {e}")
else:
# Only runs if there was NO exception in try
print(f"Success: {result}")
finally:
# Always runs — for cleanup
print("Done")
# raise — raise an exception explicitly
def divide(a, b):
if b == 0:
raise ZeroDivisionError("The divisor can't be zero")
return a / b
# raise from — exception chaining
def fetch_data(url):
try:
return requests.get(url)
except ConnectionError as e:
raise RuntimeError(f"Failed to fetch data from {url}") from e
# raise without an argument — re-raise the exception being handled
try:
critical_process()
except Exception:
logging.exception("Process failed")
raise # forward the exception to the caller
Variable Scope: global, nonlocal
#
global
#
# global — declare that a variable refers to the global scope
count = 0
def increment():
global count # without this, the assignment creates a new local variable
count += 1
increment()
increment()
print(count) # → 2
# ANTI-PATTERN: too much use of global
# Better: return values from the function and capture them outside
def clean_increment(count):
return count + 1
count = clean_increment(count)
nonlocal
#
# nonlocal — refer to a variable in the enclosing scope (not global)
def make_counter():
n = 0
def increment():
nonlocal n # refers to n in make_counter, not the global
n += 1
return n
return increment
counter = make_counter()
print(counter()) # → 1
print(counter()) # → 2
print(counter()) # → 3
Context Management: with, as
#
# with — a context manager, ensuring automatic cleanup
# Replaces the verbose try/finally pattern
# ANTI-PATTERN: opening a file without a context manager
f = open("data.txt")
data = f.read()
f.close() # can be skipped if an exception happens before!
# CORRECT: with ensures the file is always closed
with open("data.txt", "r", encoding="utf-8") as f:
data = f.read()
# f.close() is called automatically here — even if an exception occurred
# with for several context managers at once
with open("input.txt") as input_file, open("output.txt", "w") as output_file:
output_file.write(input_file.read())
# with is often used for:
# - file I/O
# - database connections
# - threading locks
# - transactions
# - mocks in testing
import threading
lock = threading.Lock()
with lock:
# thread-safe code here
modify_shared_data()
# the lock is released automatically
Deletion: del
#
# del — remove a name's binding to an object
# Delete a variable
x = 42
del x
# print(x) # → NameError: name 'x' is not defined
# Delete a list element
lst = [1, 2, 3, 4, 5]
del lst[2] # remove index 2
print(lst) # → [1, 2, 4, 5]
del lst[1:3] # remove a slice
print(lst) # → [1, 5]
# Delete a dict key
d = {"a": 1, "b": 2, "c": 3}
del d["b"]
print(d) # → {'a': 1, 'c': 3}
# Delete an object attribute
class Config:
debug = True
version = "1.0"
del Config.debug
# Config.debug # → AttributeError
# del helps the garbage collector free memory faster
big_data = list(range(10_000_000))
# ... process the data ...
del big_data # release the reference so GC can free the memory
assert — Condition Checks
#
# assert expression [, message] — raises AssertionError if the expression is False
# Used to verify assumptions during development
def divide(a, b):
assert b != 0, f"The divisor can't be zero, got: {b}"
return a / b
def process_data(data):
assert isinstance(data, list), "data must be a list"
assert len(data) > 0, "data must not be empty"
# process...
# assert is very useful in testing
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0
assertcan be disabled when Python runs with the optimization flag (python -O). Don’t useassertfor user input validation or critical business logic — useif+raiseinstead.assertis only for debugging and verifying internal assumptions.
Async Keywords: async, await
#
import asyncio
# async def — defines a coroutine function
async def fetch_data(url: str) -> str:
# await — wait for another coroutine to finish without blocking the event loop
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
# async for — async iteration
async def read_stream(stream):
async for chunk in stream:
process(chunk)
# async with — async context manager
async def with_connection():
async with create_connection() as conn:
await conn.execute("SELECT 1")
# Running a coroutine
async def main():
result = await fetch_data("https://api.example.com/data")
print(result)
asyncio.run(main())
match — Pattern Matching (Python 3.10+)
#
# match/case — structural pattern matching
# More than just a switch — it can match data structures
def process_command(command):
match command:
case "exit" | "quit":
return "Goodbye!"
case "help":
return "Available commands: exit, help, info"
case str() if command.startswith("search "):
word = command[7:]
return f"Searching: {word}"
case _:
return f"Unknown command: {command}"
# Pattern matching on data structures
def analyze_point(point):
match point:
case (0, 0):
return "Origin"
case (x, 0):
return f"On the X axis: {x}"
case (0, y):
return f"On the Y axis: {y}"
case (x, y):
return f"Point ({x}, {y})"
# Pattern matching on dicts
def http_routing(request):
match request:
case {"method": "GET", "path": path}:
return handle_get(path)
case {"method": "POST", "path": path, "body": body}:
return handle_post(path, body)
case {"method": method}:
return f"Method {method} not supported"
type — Type Aliases (Python 3.12+)
#
# type — defines a type alias (Python 3.12+)
# Replaces: AliasName = type or AliasName: TypeAlias = type
type Vector = list[float]
type Matrix = list[Vector]
type Callback = Callable[[int, str], bool]
# Usage in functions
def normalize(v: Vector) -> Vector:
length = sum(x**2 for x in v) ** 0.5
return [x / length for x in v]
# Before Python 3.12, TypeAlias from typing was used
from typing import TypeAlias
Vector: TypeAlias = list[float]
from in the raise Context
#
# from in raise — exception chaining (not import)
try:
data = json.loads(text)
except json.JSONDecodeError as e:
# Wrap in a domain exception, but keep the original context
raise ValueError(f"Invalid data format: {text!r}") from e
# from None — hide the original context
try:
value = d["key"]
except KeyError:
raise KeyError(f"Key 'key' not found") from None
Quick Reference Table #
| Keyword | Category | Main Function |
|---|---|---|
True | Value | Boolean true |
False | Value | Boolean false |
None | Value | Absence of a value |
and | Logic | Short-circuit AND |
or | Logic | Short-circuit OR |
not | Logic | Boolean negation |
is | Identity | Check the same object in memory |
in | Membership | Check an element in a collection / iterate |
if | Flow control | Conditional branching |
elif | Flow control | Alternative branching |
else | Flow control | Fallback / else on loops |
for | Loops | Iterate over an iterable |
while | Loops | Repeat while the condition is True |
break | Loops | Stop the loop |
continue | Loops | Skip to the next iteration |
pass | Placeholder | Do nothing (required syntax) |
def | Definitions | Define a function |
class | Definitions | Define a class |
lambda | Definitions | One-expression anonymous function |
return | Functions | Return a value from a function |
yield | Generators | Produce a value (lazily), create a generator |
import | Modules | Import a module |
from | Module / raise | Import specifics / exception chaining |
as | Module / with | Give an alias |
try | Error handling | Block that may raise an exception |
except | Error handling | Catch an exception |
raise | Error handling | Raise an exception |
finally | Error handling | Always runs (cleanup) |
assert | Debugging | Verify an assumption — raises AssertionError |
del | Memory | Delete a variable / element / attribute |
global | Scope | Refer to a variable in the global scope |
nonlocal | Scope | Refer to a variable in the enclosing scope |
with | Context manager | Automatic resource management |
async | Async | Define a coroutine |
await | Async | Wait for a coroutine to finish |
match | Pattern matching | Structural pattern matching (3.10+) |
type | Types | Type alias (3.12+) |
Summary #
Noneis always compared withis/is not— not==or!=.Noneis a singleton and object identity is what you want to check.andandoraren’t just boolean — both return one of the operands based on short-circuit evaluation, not alwaysTrue/False.isis only forNone,True,False— don’t useisto compare ints, strings, or other objects because the results are inconsistent due to object caching.passvs...(Ellipsis) —passfor intentionally empty blocks;...is more common in type stubs, ABCs, and as a more expressive placeholder.elseon loops — runs only if the loop finishes withoutbreak— a clean idiom for the “search and report if not found” pattern.globalshould be avoided — return values from functions rather than modifying global variables. Usenonlocalfor state inside closures.assertisn’t for production validation — it can be disabled with-O. Useif+raisefor real input validation.withfor every resource — files, database connections, locks, and anything needing cleanup — safer than manualtry/finally.match(3.10+) is far more powerful thanswitch— it can match tuple, dict, and object structures while extracting their values.yieldturns a function into a lazy generator — values are produced one at a time as needed, very efficient for large data.