Python's Lesser-Used Built-ins — Cheat Sheet

zip() — pair up multiple iterables

names = ["Sam", "Ali", "Jo"]
ages = [30, 25, 40]

for name, age in zip(names, ages):
    print(name, age)
# Sam 30 / Ali 25 / Jo 40

Cool use cases:

# Build a dict from two lists
dict(zip(names, ages))   # {"Sam": 30, "Ali": 25, "Jo": 40}

# Transpose a matrix (list of rows -> list of columns)
matrix = [[1, 2, 3], [4, 5, 6]]
list(zip(*matrix))       # [(1, 4), (2, 5), (3, 6)]

# Pairwise iteration — compare each element to the next
nums = [1, 3, 6, 10]
[b - a for a, b in zip(nums, nums[1:])]   # [2, 3, 4] (diffs between consecutive items)

# zip stops at the shortest iterable — use itertools.zip_longest to pad instead
from itertools import zip_longest
list(zip_longest([1, 2], [1, 2, 3], fillvalue=0))  # [(1,1), (2,2), (0,3)]

map() — apply a function to every item

nums = [1, 2, 3]
list(map(str, nums))          # ["1", "2", "3"]
list(map(lambda x: x * 2, nums))  # [2, 4, 6]

Cool use cases:

# map with multiple iterables — applies function pairwise
list(map(lambda x, y: x + y, [1, 2], [10, 20]))  # [11, 22]

# Convert a row of strings to ints in one line (classic parsing pattern)
line = "3 1 4 1 5"
nums = list(map(int, line.split()))   # [3, 1, 4, 1, 5]

In practice, a list/generator comprehension ([int(x) for x in line.split()]) is often considered more "Pythonic" than map() — but map() is handy for quick one-liners with an existing function, and it's lazy (doesn't build the list until you ask).


filter() — keep items that pass a test

nums = [1, 2, 3, 4, 5, 6]
list(filter(lambda x: x % 2 == 0, nums))   # [2, 4, 6]

# filter(None, iterable) drops all falsy values — quick cleanup trick
messy = [0, "hi", "", None, "bye", False, 42]
list(filter(None, messy))   # ["hi", "bye", 42]

enumerate() — index + value together

for i, val in enumerate(["a", "b", "c"]):
    print(i, val)
# 0 a / 1 b / 2 c

# Start counting from a different number
for i, val in enumerate(["a", "b", "c"], start=1):
    print(i, val)
# 1 a / 2 b / 3 c

Beats for i in range(len(lst)): lst[i] — more readable, no manual indexing.


any() / all() — quick boolean checks over an iterable

nums = [2, 4, 6, 7]
any(n % 2 != 0 for n in nums)   # True  -> at least one odd number
all(n % 2 == 0 for n in nums)   # False -> not all are even

# Combined with a generator expression, no need to build a list first
all(len(word) > 2 for word in ["cat", "dog", "ox"])   # False ("ox" is too short)

Both short-circuit — any() stops at the first True, all() stops at the first False.


sorted() with key= — see the earlier operators cheat sheet, but worth repeating here since it pairs so well with lambda

sorted(["banana", "kiwi", "fig"], key=len)

functools.reduce() — fold a list down to one value

from functools import reduce

nums = [1, 2, 3, 4]
reduce(lambda acc, x: acc + x, nums)        # 10 (sum, but manual)
reduce(lambda acc, x: acc * x, nums)        # 24 (product)
reduce(lambda acc, x: max(acc, x), nums)    # 4  (max, but manual)

Rarely needed — sum(), max(), min() already cover the common cases — but useful when the "combine" logic is custom.


itertools — the toolbox for combinatorics & iteration patterns

from itertools import chain, combinations, permutations, groupby, count, cycle, islice

# chain: flatten multiple iterables into one stream
list(chain([1, 2], [3, 4]))                 # [1, 2, 3, 4]

# combinations: all unique groupings, order doesn't matter
list(combinations([1, 2, 3], 2))            # [(1,2), (1,3), (2,3)]

# permutations: all orderings
list(permutations([1, 2], 2))               # [(1,2), (2,1)]

# groupby: group consecutive items by a key (input must already be sorted by that key!)
data = [("a", 1), ("a", 2), ("b", 3)]
for key, group in groupby(data, key=lambda x: x[0]):
    print(key, list(group))
# a [('a', 1), ('a', 2)]
# b [('b', 3)]

# count / cycle: infinite iterators — always pair with islice or a break condition
list(islice(count(10, 2), 5))   # [10, 12, 14, 16, 18]  (start=10, step=2, take 5)

collections — specialized containers worth knowing

from collections import Counter, defaultdict, namedtuple

# Counter: frequency counting in one line
Counter("mississippi")   # Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
Counter("mississippi").most_common(2)   # [('i', 4), ('s', 4)]

# defaultdict: no more "if key not in dict" boilerplate
groups = defaultdict(list)
for word in ["apple", "banana", "avocado"]:
    groups[word[0]].append(word)
# defaultdict(list, {'a': ['apple', 'avocado'], 'b': ['banana']})

# namedtuple: lightweight class-like tuple with named fields
Point = namedtuple("Point", ["x", "y"])
p = Point(1, 2)
p.x, p.y   # 1, 2

Quick reference table

| Function | Use it when... | |---|---| | zip() | You need to walk multiple lists in lockstep | | map() | Applying one existing function to every item | | filter() | Keeping items that pass a condition | | enumerate() | You need both index and value in a loop | | any() / all() | Quick True/False check across a collection | | reduce() | Folding a list into a single value with custom logic | | itertools.chain | Flattening several iterables into one loop | | itertools.groupby | Grouping consecutive items (sort first!) | | Counter | Frequency counts | | defaultdict | Building groups/buckets without key-checking boilerplate |

Python Operators Cheat Sheet

1. Numeric Operators

| Operator | Name | Example | Result | |---|---|---|---| | + | Addition | 5 + 2 | 7 | | - | Subtraction | 5 - 2 | 3 | | * | Multiplication | 5 * 2 | 10 | | / | True division | 5 / 2 | 2.5 | | // | Floor division | 5 // 2 | 2 | | % | Modulo (remainder) | 5 % 2 | 1 | | ** | Exponent | 5 ** 2 | 25 | | -x | Unary negation | -5 | -5 |

Augmented assignment (shorthand for updating a variable):

x = 5
x += 3   # x = x + 3  -> 8
x -= 1   # x = x - 1  -> 7
x *= 2   # x = x * 2  -> 14
x /= 2   # x = x / 2  -> 7.0
x //= 2  # x = x // 2 -> 3.0
x **= 2  # x = x ** 2 -> 9.0
x %= 4   # x = x % 4  -> 1.0

Comparison operators (return bool, used constantly alongside logic ops):

| Operator | Meaning | |---|---| | == | equal to | | != | not equal to | | > | greater than | | < | less than | | >= | greater than or equal to | | <= | less than or equal to |

Gotcha: == compares value, is compares identity (same object in memory). Use == for numbers/strings, is for None checks (x is None).


2. Logic (Boolean) Operators

| Operator | Meaning | Example | Result | |---|---|---|---| | and | True if both sides are True | True and False | False | | or | True if at least one side is True | True or False | True | | not | Flips the value | not True | False |

Short-circuit evaluation — Python stops as soon as the result is known:

def loud(val):
    print("called")
    return val

False and loud(True)   # "called" never prints — left side already False
True or loud(False)    # "called" never prints — left side already True

Truthy / falsyand/or don't just return True/False, they return one of the actual operands:

0 or "default"     # -> "default"   (0 is falsy)
"" or "fallback"    # -> "fallback"  (empty string is falsy)
[] or [1, 2]        # -> [1, 2]      (empty list is falsy)
5 and 10             # -> 10          (both truthy -> returns last)

Falsy values: False, None, 0, 0.0, "", [], {}, (), set().

Membership & identity (often paired with logic ops):

"a" in "cat"        # True
3 not in [1, 2]      # True
x is None            # identity check
x is not None

3. Set Operators

| Operator | Name | Example | Result | |---|---|---|---| | \| | Union | {1, 2} \| {2, 3} | {1, 2, 3} | | & | Intersection | {1, 2} & {2, 3} | {2} | | - | Difference | {1, 2} - {2, 3} | {1} | | ^ | Symmetric difference | {1, 2} ^ {2, 3} | {1, 3} |

Method equivalents (more readable, and take any iterable, not just a set):

a = {1, 2}
b = {2, 3}
a.union(b)               # same as a | b
a.intersection(b)        # same as a & b
a.difference(b)          # same as a - b
a.symmetric_difference(b) # same as a ^ b

There are also in-place versions: a |= b, a &= b, a -= b, a ^= b (update a directly).

Note: |, &, ^ also work on ints as bitwise operators (bitwise OR/AND/XOR) — same symbols, different meaning depending on the operand types.


4. Sorting Data Structures

sorted() — works on any iterable, always returns a new list (original untouched):

nums = [3, 1, 2]
sorted(nums)              # [1, 2, 3]
sorted(nums, reverse=True) # [3, 2, 1]
nums                       # still [3, 1, 2] — unchanged

.sort() — list method, sorts in place, returns None:

nums = [3, 1, 2]
nums.sort()          # nums is now [1, 2, 3]
result = nums.sort()  # result is None — common bug!

key= parameter — sort by something other than natural order:

words = ["banana", "kiwi", "fig"]
sorted(words, key=len)                # ["fig", "kiwi", "banana"]  (by length)

people = [{"name": "Sam", "age": 30}, {"name": "Ali", "age": 25}]
sorted(people, key=lambda p: p["age"])  # sorted by age

# sort by multiple criteria: age first, then name
sorted(people, key=lambda p: (p["age"], p["name"]))

Other structures:

# Dict: sort by keys or values (dicts have no native .sort())
d = {"b": 2, "a": 1}
dict(sorted(d.items()))                    # sort by key -> {"a": 1, "b": 2}
dict(sorted(d.items(), key=lambda kv: kv[1]))  # sort by value

# Set: no order to preserve, but you can produce a sorted list from one
sorted({3, 1, 2})   # [1, 2, 3]

# Tuple: same idea, sorted() always returns a list
sorted((3, 1, 2))   # [1, 2, 3]

Quick rule of thumb: need the original order kept? Use sorted(). Just reordering a list you own and don't need the old order? .sort() is slightly more efficient (no new list allocated).