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
assert can be disabled when Python runs with the optimization flag (python -O). Don’t use assert for user input validation or critical business logic — use if + raise instead. assert is 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 #

KeywordCategoryMain Function
TrueValueBoolean true
FalseValueBoolean false
NoneValueAbsence of a value
andLogicShort-circuit AND
orLogicShort-circuit OR
notLogicBoolean negation
isIdentityCheck the same object in memory
inMembershipCheck an element in a collection / iterate
ifFlow controlConditional branching
elifFlow controlAlternative branching
elseFlow controlFallback / else on loops
forLoopsIterate over an iterable
whileLoopsRepeat while the condition is True
breakLoopsStop the loop
continueLoopsSkip to the next iteration
passPlaceholderDo nothing (required syntax)
defDefinitionsDefine a function
classDefinitionsDefine a class
lambdaDefinitionsOne-expression anonymous function
returnFunctionsReturn a value from a function
yieldGeneratorsProduce a value (lazily), create a generator
importModulesImport a module
fromModule / raiseImport specifics / exception chaining
asModule / withGive an alias
tryError handlingBlock that may raise an exception
exceptError handlingCatch an exception
raiseError handlingRaise an exception
finallyError handlingAlways runs (cleanup)
assertDebuggingVerify an assumption — raises AssertionError
delMemoryDelete a variable / element / attribute
globalScopeRefer to a variable in the global scope
nonlocalScopeRefer to a variable in the enclosing scope
withContext managerAutomatic resource management
asyncAsyncDefine a coroutine
awaitAsyncWait for a coroutine to finish
matchPattern matchingStructural pattern matching (3.10+)
typeTypesType alias (3.12+)

Summary #

  • None is always compared with is/is not — not == or !=. None is a singleton and object identity is what you want to check.
  • and and or aren’t just boolean — both return one of the operands based on short-circuit evaluation, not always True/False.
  • is is only for None, True, False — don’t use is to compare ints, strings, or other objects because the results are inconsistent due to object caching.
  • pass vs ... (Ellipsis)pass for intentionally empty blocks; ... is more common in type stubs, ABCs, and as a more expressive placeholder.
  • else on loops — runs only if the loop finishes without break — a clean idiom for the “search and report if not found” pattern.
  • global should be avoided — return values from functions rather than modifying global variables. Use nonlocal for state inside closures.
  • assert isn’t for production validation — it can be disabled with -O. Use if + raise for real input validation.
  • with for every resource — files, database connections, locks, and anything needing cleanup — safer than manual try/finally.
  • match (3.10+) is far more powerful than switch — it can match tuple, dict, and object structures while extracting their values.
  • yield turns a function into a lazy generator — values are produced one at a time as needed, very efficient for large data.

← Previous: Virtual Environments   Next: Multithreading →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact