The Python Idioms Worth Memorizing: A Practical Python Cheat Sheet
A practical Python cheat sheet for the idioms you reach for daily: comprehensions, slicing, f-strings, enumerate, zip, with statements, and core data structures.
The Python Idioms Worth Memorizing: A Practical Python Cheat Sheet
I have written Python for years, and I still keep a short list of idioms taped to the inside of my head. Not the language spec, not the standard library index, just the dozen or so patterns I type every single day. When you internalize those, reading and writing Python stops feeling like translation. This post walks through the ones I reach for most, with real snippets you can paste straight into a REPL.
Comprehensions replace the loop you were about to write
The list comprehension is the most Pythonic construct in the language, and once it clicks you stop writing for loops to build lists. The shape is [expression for item in iterable], optionally with a filter:
nums = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in nums] # [2, 4, 6, 8, 10]
evens = [x for x in nums if x % 2 == 0] # [2, 4]
You can ternary inside the expression to map values, and you can filter at the end to drop them. Keep the two straight: the if after the for filters; the if/else before the for maps.
labels = ["even" if x % 2 == 0 else "odd" for x in nums]
Dict comprehensions follow the same grammar with a key: value pair. This one inverts a mapping in a single line:
prices = {"apple": 3, "pear": 5}
by_price = {v: k for k, v in prices.items()} # {3: "apple", 5: "pear"}
Set comprehensions exist too, with braces and no colon: {len(w) for w in words} gives you the distinct word lengths. When the data is large and you only iterate once, swap the brackets for parentheses to get a generator expression that uses constant memory: sum(x * x for x in range(1_000_000)) never builds the million-element list.
A worked example: rewriting a loop
Here is a loop I see in code review all the time. Someone wants the uppercase names of active users:
result = []
for user in users:
if user.active:
result.append(user.name.upper())
Four lines, a mutable accumulator, and an append call on every pass. The comprehension says the same thing in one line, and reads top to bottom as "give me the uppercased name for each active user":
result = [user.name.upper() for user in users if user.active]
The rewrite is not just shorter. It signals intent. A reader sees [... for ... if ...] and knows immediately you are building a new list by filtering and mapping, with no hidden side effects in the loop body. That is the whole point of an idiom: it compresses a familiar shape into a glance.
f-strings are the only formatting you need
Forget % formatting and str.format. f-strings interpolate directly and support a format spec after a colon:
name, total = "Ada", 1729.5
print(f"{name} owes {total:.2f}") # Ada owes 1729.50
print(f"{total:>10,.2f}") # right-aligned, thousands sep
The debugging form f"{total=}" prints total=1729.5, label and value together. I use it constantly instead of bare print calls when tracing a value through a function.
enumerate and zip kill the index loop
If you ever write for i in range(len(xs)), stop. enumerate gives you the index and the value at once:
for i, v in enumerate(lst):
print(i, v)
for i, v in enumerate(lst, start=1): # 1-based counting
...
zip walks two or more sequences in lockstep, which is how you iterate parallel lists without indexing:
names = ["Ada", "Linus", "Guido"]
langs = ["Ada", "C", "Python"]
for name, lang in zip(names, langs):
print(f"{name} -> {lang}")
In 3.10+, pass strict=True to zip so it raises instead of silently truncating when the inputs are different lengths. That has caught real bugs for me where one list was a row short.
Slicing is a tiny language of its own
The slice form is seq[start:stop:step], every part optional. Once you read it fluently you stop writing helper loops to reverse or chunk:
s = "abcdef"
s[1:4] # "bcd"
s[:3] # "abc"
s[-2:] # "ef" (last two)
s[::-1] # "fedcba" (reversed)
s[::2] # "ace" (every other)
Negative indices count from the end, and slices never raise on out-of-range bounds, which makes them safe for defensive trimming.
with statements own your cleanup
Any time you open a resource, wrap it in with. The context manager guarantees the file closes even if the body raises. And always pass an explicit encoding, because the platform default will eventually bite you on a Windows box:
with open("data.txt", encoding="utf-8") as f:
text = f.read()
You can open several in one statement, and the same pattern covers locks, database connections, and tempfile handles. The rule of thumb: if something has an open and a close, it belongs in a with.
The data structures, at a glance
- list — ordered, mutable, your default sequence.
.appendadds one item;.extendadds each item of an iterable (mixing them up is a classic trap). - dict — key/value mapping. Reach for
.get(key, default)instead of atry/except KeyError, and merge witha | bin 3.9+. - set — distinct items with
|(union),&(intersection),-(difference),^(symmetric difference). Deduplicate a list withlist(dict.fromkeys(xs))when you need to preserve order, orset(xs)when you do not. - tuple — immutable record. A single-element tuple needs the trailing comma:
(1,), not(1). - collections.Counter — tally anything:
Counter(words).most_common(3)returns the three most frequent items.
Where I keep all of this
Nobody remembers every format spec or itertools signature, and you should not try. I keep the full searchable reference open in a tab and type the keyword when I blank on a pattern: Python Cheat Sheet has 100+ idiomatic snippets across fifteen categories, plus a dedicated pitfalls section covering the mutable default argument, late-binding closures, and why -7 // 2 == -4. Type "comprehension" and every list, dict, and set comprehension entry surfaces at once.
Python rarely works alone, so I keep its neighbors close too. When I am shaping query results into Python objects I bounce over to the SQL Cheat Sheet; when I am cleaning up a feature branch mid-task I reach for the Git Cheat Sheet; and when a string-parsing snippet needs a pattern I check the Regex Cheat Sheet. Same searchable layout, same one-static-page-no-uploads design, so muscle memory carries across all of them.
Memorize the dozen idioms above and you will read most Python in the wild without stopping. Keep the cheat sheet open for the other ninety.
Made by Toolora · Updated 2026-06-13