Python Cheat Sheet Every Developer Should Save (Syntax, Idioms and Traps)
β‘ Quick Answer
A practical Python cheat sheet covering syntax, data structures, comprehensions, the standard library and the mutable-default trap that catches everyone.
Get more content like this on Telegram!
Daily AI tips, notes & resources β free
Advertisement
Python Cheat Sheet Every Developer Should Save
Python's syntax is not the hard part. The standard library and the idioms are.
Any developer can write a for loop in Python within an hour. What separates code that works from code that reads like Python is knowing that collections.Counter already solves the thing you were about to write forty lines for.
This sheet covers the syntax you look up, the idioms worth adopting, and the four traps that catch nearly everyone.
Updated for 2026. Covers Python 3.10 and later.
Data Structures at a Glance
Choosing wrongly here is the most common cause of slow Python.
| Type | Ordered | Mutable | Lookup speed | Use for |
|---|---|---|---|---|
list | Yes | Yes | O(n) | Sequences you modify |
tuple | Yes | No | O(n) | Fixed records, dict keys |
set | No | Yes | O(1) | Uniqueness, membership tests |
dict | Yes* | Yes | O(1) | Lookup by key |
* Dictionaries preserve insertion order from Python 3.7 onward.
The line that matters: in on a list is O(n); in on a set is O(1).
# slow β scans the whole list every time
if user_id in list_of_million_ids:
# fast β hash lookup, constant time
valid_ids = set(list_of_million_ids)
if user_id in valid_ids:If you have ever written a loop that checks membership against a growing list and wondered why it crawled, this is why.
Strings
name = "Python"
f"Hello {name}" # f-string β use this
f"{price:.2f}" # 2 decimal places
f"{count:,}" # thousands separator: 1,234,567
f"{total=}" # debug: prints "total=42"
name.strip() # remove surrounding whitespace
name.lower() / name.upper()
name.replace("a", "b")
name.split(",") # string β list
",".join(items) # list β string
name.startswith("Py")
"py" in name.lower() # substring check",".join(items) is the one people get backwards. The separator is the string you call the method on, not the list. It reads oddly the first hundred times and then becomes natural.
On f"{total=}": added in Python 3.8, this prints the variable name and its value together. It replaces print("total:", total) and is meaningfully faster to type while debugging.
Lists
items = [3, 1, 2]
items.append(4) # add to end
items.insert(0, 0) # insert at index
items.extend([5, 6]) # add multiple
items.pop() # remove and return last
items.remove(3) # remove first matching value
items.sort() # sort in place
sorted(items, reverse=True) # return a new sorted list
items[::-1] # reversed copy
items[1:3] # slice
len(items)sort() versus sorted(): list.sort() modifies the list and returns None. sorted(list) leaves the original alone and returns a new list. Writing items = items.sort() sets items to None, which is a bug that appears roughly once in every beginner's first month.
Sorting by a key is the pattern worth memorising:
users.sort(key=lambda u: u["age"])
sorted(users, key=lambda u: (u["last"], u["first"])) # multi-level sortDictionaries
user = {"name": "Ada", "age": 36}
user["name"] # KeyError if missing
user.get("email") # None if missing
user.get("email", "n/a") # default if missing
user.setdefault("tags", []) # set only if absent
user.keys() / user.values() / user.items()
for key, value in user.items():
...
{**a, **b} # merge two dicts
a | b # merge (Python 3.9+)Use .get() rather than square brackets whenever the key might be missing. The square-bracket form raises KeyError and crashes; .get() returns None or your chosen default. This one habit removes a large share of production exceptions in code that handles external data.
Comprehensions
[x * 2 for x in items] # list
[x for x in items if x > 0] # with a filter
[x * 2 if x > 0 else 0 for x in items] # with a conditional value
{k: v for k, v in pairs} # dict comprehension
{x for x in items} # set comprehension
(x * 2 for x in items) # generator β lazy, memory-efficientNote the position difference: a filter (if x > 0) goes at the end; a conditional value (if/else) goes at the front. Getting these confused is a common source of syntax errors.
When to stop. A comprehension that needs a comment has stopped being an improvement. If you have two for clauses and an if, write the loop β the loop will be read correctly by everyone, and the comprehension will not.
Generators (round brackets) versus lists (square brackets): a generator produces items one at a time instead of building the whole collection in memory. For a ten-million-row file this is the difference between working and running out of RAM.
Functions
def greet(name, greeting="Hello"):
return f"{greeting}, {name}"
def total(*args): # any number of positional arguments
return sum(args)
def config(**kwargs): # any number of keyword arguments
return kwargs
def add(a: int, b: int) -> int: # type hints
return a + b
square = lambda x: x ** 2 # inline functionThe trap that catches everyone:
# WRONG β the list is created once, at definition
def add_item(item, items=[]):
items.append(item)
return items
add_item("a") # ['a']
add_item("b") # ['a', 'b'] β the previous call leaked
# RIGHT
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return itemsThe default value is evaluated once, when the def statement runs β not on each call. Every call that omits the argument shares the same object. This applies to lists, dicts, sets, and any mutable object.
It is worth reading that twice, because the wrong version looks completely reasonable and only misbehaves from the second call onward.
Loops
for item in items: ...
for i, item in enumerate(items): ... # index + value
for i, item in enumerate(items, 1): ... # start counting at 1
for a, b in zip(list_a, list_b): ... # parallel iteration
for key, value in mapping.items(): ...
while condition: ...
break # exit the loop
continue # skip to the next iteration
else: # runs only if the loop was not broken out ofenumerate replaces the manual counter variable, and zip replaces indexing into two lists at once. Both are immediate signs of someone who writes Python rather than translating another language into Python.
Error Handling
try:
result = risky()
except ValueError as e:
print(f"Bad value: {e}")
except (TypeError, KeyError):
handle_both()
except Exception as e:
log(e)
raise # re-raise after logging
else:
print("no exception occurred")
finally:
cleanup() # always runsNever write a bare except:. It catches KeyboardInterrupt and SystemExit as well, which means your program cannot be stopped with Ctrl-C and will not shut down cleanly. If you genuinely need to catch everything, write except Exception:, which excludes those two.
Catch the specific exception you expect. A broad except Exception around a block that could fail six ways will silently swallow the five failures you did not anticipate, and you will find out months later.
Files
with open("file.txt") as f:
content = f.read() # whole file as a string
with open("file.txt") as f:
for line in f: # line by line β memory-safe
process(line)
with open("out.txt", "w") as f: # "w" overwrites, "a" appends
f.write("text")
import json
with open("data.json") as f:
data = json.load(f)Always use with. It closes the file automatically, including when an exception is raised partway through. Calling open() without it means a file handle leaks on every error path.
Iterate rather than read() for anything that might be large. f.read() loads the entire file into memory; iterating over f reads one line at a time.
The Standard Library Worth Knowing
This is the section that separates Python you wrote from Python you should have written.
from collections import Counter, defaultdict, deque
Counter(words).most_common(3) # top 3 most frequent
defaultdict(list) # dict where missing keys default to []
deque(maxlen=100) # fast appends at both ends
from itertools import chain, groupby, combinations
list(chain(a, b, c)) # flatten several iterables
from pathlib import Path
Path("data") / "file.txt" # OS-safe path joining
Path("file.txt").exists()
Path("file.txt").read_text()
from datetime import datetime, timedelta
datetime.now() + timedelta(days=7)Counter replaces the "count occurrences with a dictionary" loop that everyone writes at least once. defaultdict(list) replaces the "check if the key exists before appending" pattern. pathlib replaces string concatenation for file paths and works correctly on Windows without you thinking about it.
Each of these turns eight lines into one, and each is already installed.
Modern Syntax
# walrus operator β assign inside an expression (3.8+)
if (n := len(items)) > 10:
print(f"{n} items is too many")
# dictionary merge (3.9+)
merged = defaults | overrides
# structural pattern matching (3.10+)
match command:
case "start":
begin()
case "stop" | "halt":
end()
case _:
unknown()Script Structure
def main():
...
if __name__ == "__main__":
main()__name__ is "__main__" when the file is run directly and the module's name when it is imported. Without this guard, importing your file to reuse one function also runs everything at the bottom of it β starting servers, printing output, and generally surprising whoever imported it.
The Four Traps
1. Mutable default arguments. Covered above. The most common Python bug that passes code review.
2. items = items.sort(). sort() returns None. Use sorted() if you want a return value.
3. Modifying a list while iterating over it. Removing items during a for loop skips elements, because the indices shift underneath you. Iterate over a copy β for x in items[:] β or build a new list with a comprehension.
4. is versus ==. == compares values; is compares identity. Use is only for None, True, and False. a is b on two equal strings or numbers may be True or False depending on interpreter caching, which makes it a wonderfully unreliable bug.
Print This Section
LOOKUP set/dict = O(1) Β· list = O(n) β use sets for membership
STRINGS f"{x}" Β· f"{p:.2f}" Β· f"{x=}" Β· ",".join(items)
DICTS .get(k, default) not d[k]
SORT sorted(x, key=lambda i: i["f"]) sort() returns None
FILES with open(...) as f: and iterate, do not .read()
STDLIB Counter Β· defaultdict(list) Β· pathlib.Path
TRAP def f(x, items=None): β never items=[]Python rewards knowing the standard library far more than it rewards knowing syntax. The next time you are about to write twenty lines, check whether collections or itertools already did it.
π Next in this collection: SQL Cheat Sheet for Interviews and Real Projects, or return to The Complete Developer Cheat Sheet Collection.
Advertisement
π¬ DiscussionPowered by GitHub Discussions
Frequently Asked Questions

AI & Software Engineering Editorial Team
The AiTechWorlds editorial team writes and reviews in-depth guides on artificial intelligence, machine learning, prompt engineering, programming, and developer tools. Every article is fact-checked against primary sources and kept up to date for working developers and CS students.
Not sure yet? Ask AI about this article
Get an instant, unbiased AI summary of βPython Cheat Sheet Every Developer Should Save (Syntax, Idioms and Traps)β.
Advertisement
Related Articles
Bash Scripting Cheat Sheet (The First Line Every Script Needs)
A practical Bash scripting cheat sheet β variables, conditionals, loops, functions, and the set -euo pipefail line that turns silent failure into loud failure.
15 Coding Interview Patterns That Solve Most Problems
The 15 coding interview patterns that cover most problems β the signal that identifies each one, a minimal code skeleton, and a two-minute recognition table.
CSS and Tailwind Cheat Sheet: Flexbox, Grid and Centering Solved
A practical CSS and Tailwind cheat sheet β Flexbox vs Grid decision rules, every centering method, responsive breakpoints, and the specificity traps.
Data Structures and Big-O Cheat Sheet for Coding Interviews
A practical big O cheat sheet β the complexity ladder with real examples, data structure operation tables, and how to state your complexity in an interview.