Filter concepts by levelShowing all levels.

Python · Fundamentals

Built-ins worth knowing well

The functions reached for in nearly every loop, filter, and type check

  • enumerate + zipindex and pair, without a manual counter
  • map / filter are lazynothing runs until consumed
  • sorted() never mutatesalways a new list — list.sort() is the in-place twin
  • getattr/setattr/hasattrattribute access by a NAME chosen at runtime

1.enumerate

enumerate(iterable) wraps any iterable in a lazy sequence of (index, item) pairs, starting at 0 by default. It replaces the manual counter loop with one built into the language.

Key features

  • enumerate(iterable) yields (index, item) tuples, lazily, one at a time
  • Indexing starts at 0 by default — pass start=N to begin anywhere else
  • Unpack directly in a for loop: for i, item in enumerate(seq)
  • Works on any iterable, not just lists — strings, dicts, generators too
  • list(enumerate(seq)) materializes every pair if a real list is needed

enumerate over ["a", "b", "c"]

enumerate over ["a", "b", "c"]
CallResult (materialized)
list(enumerate(["a", "b", "c"]))[(0, 'a'), (1, 'b'), (2, 'c')]
list(enumerate(["a", "b", "c"], start=1))[(1, 'a'), (2, 'b'), (3, 'c')]
list(enumerate("hi"))[(0, 'h'), (1, 'i')]
next(iter(enumerate(["a", "b"])))(0, 'a') — one pair at a time, lazily
Each item leaves paired with its position

an iterable

["a", "b", "c"]

enumerate() pairs each item

with a running index, lazily

(index, item) tuples

(0, 'a'), (1, 'b'), (2, 'c')

  1. an iterable — ["a", "b", "c"]
  2. enumerate() pairs each item — with a running index, lazily
  3. (index, item) tuples — (0, 'a'), (1, 'b'), (2, 'c')

Command

python
for index, item in enumerate(sequence):        # index starts at 0
    ...

for index, item in enumerate(sequence, start=1):  # index starts at 1
    ...

enumerate(iterable, start=0) yields lazy (index, item) pairs — unpack them directly in a for loop instead of maintaining a manual counter.

2.zip

zip(*iterables) pairs up items from two or more iterables position by position, lazily, and stops as soon as the shortest input is exhausted. zip(a, b) is the standard way to loop over two sequences together.

Key features

  • zip(a, b, ...) yields tuples pairing item i from every iterable, for each position
  • Stops at the SHORTEST input — extra items in longer iterables are silently dropped
  • zip() with no arguments yields nothing; zip(a) yields one-tuples
  • itertools.zip_longest pads short iterables instead of truncating, if that is needed
  • dict(zip(keys, values)) is the standard idiom for building a dict from two parallel lists

zip over lists of different lengths

zip over lists of different lengths
CallResult (materialized)
list(zip([1, 2, 3], ["a", "b", "c"]))[(1, 'a'), (2, 'b'), (3, 'c')]
list(zip([1, 2, 3], ["a", "b"]))[(1, 'a'), (2, 'b')] — third item of the longer list dropped
list(zip([1, 2], ["a", "b"], [True, False]))[(1, 'a', True), (2, 'b', False)] — any number of iterables
dict(zip(["x", "y"], [1, 2])){'x': 1, 'y': 2}
Two sequences walked in lockstep

two iterables

["Ann", "Bo"], [90, 85]

zip() pairs by position

stops at the shorter one

tuples, one per position

('Ann', 90), ('Bo', 85)

  1. two iterables — ["Ann", "Bo"], [90, 85]
  2. zip() pairs by position — stops at the shorter one
  3. tuples, one per position — ('Ann', 90), ('Bo', 85)

Command

python
for a, b in zip(list_a, list_b):     # walks both together, stops at the shorter
    ...

paired = list(zip(list_a, list_b))   # materialize if a real list is needed

zip(*iterables) pairs items by position and stops at the shortest input — no error, no padding, unless itertools.zip_longest is used instead.

3.sorted

sorted(iterable, key=None, reverse=False) returns a new sorted list built from any iterable, without changing the original. key names a function called on each item to decide sort order; reverse=True sorts descending.

Key features

  • sorted(iterable) returns a NEW list — the original iterable is never modified
  • Works on any iterable, not just lists — a tuple, a set, a dict (sorts its keys), a string
  • key=func sorts by func(item) instead of the items, without changing what is returned
  • reverse=True sorts descending — not the same as sorting then reversing when there are ties
  • list.sort() is the in-place sibling — sorts the list itself and returns None

sorted() variations

sorted() variations
CallResult
sorted([3, 1, 2])[1, 2, 3]
sorted(["banana", "kiwi", "fig"], key=len)['fig', 'kiwi', 'banana'] — sorted by length
sorted([3, 1, 2], reverse=True)[3, 2, 1]
sorted({"b": 2, "a": 1})['a', 'b'] — sorting a dict sorts its keys
sorted() vs list.sort()

sorted(words)

  • Returns a brand-new sorted list
  • The original iterable is left untouched
  • Works on any iterable — tuple, set, dict, string

words.sort()

  • Sorts the list in place, returns None
  • The original list itself is reordered
  • Only exists on list — not on tuples or sets
  • sorted(words)
    • Returns a brand-new sorted list
    • The original iterable is left untouched
    • Works on any iterable — tuple, set, dict, string
  • words.sort()
    • Sorts the list in place, returns None
    • The original list itself is reordered
    • Only exists on list — not on tuples or sets

Command

python
sorted(iterable)                          # ascending, natural order
sorted(iterable, key=len)                 # ascending, by a derived value
sorted(iterable, key=len, reverse=True)   # descending, by a derived value

sorted(iterable, key=None, reverse=False) always returns a new list and never touches the original — list.sort() is the in-place version that returns None.

4.any and all

any(iterable) is True if at least one item is truthy; all(iterable) is True only if every item is. Both stop as soon as the answer is decided, and both return True/False on an empty iterable in the way their name implies logically.

Key features

  • any(iterable) is True if at least one item is truthy, else False
  • all(iterable) is True only if every item is truthy, else False
  • Both short-circuit — stop checking as soon as the result is certain
  • any([]) is False (nothing was found true); all([]) is True (nothing was found false)
  • Usually paired with a generator expression: any(x > 10 for x in nums)

any() and all() over different inputs

any() and all() over different inputs
CallResult
any([0, "", None])False — every item is falsy
any([0, "", "hi"])True — "hi" is truthy
all([1, "x", True])True — every item is truthy
all([1, 0, "x"])False — 0 is falsy
any([]) / all([])False / True — the empty-iterable edge cases
One truthy item vs zero falsy items

an iterable of values

[0, "", "hi"]

any(): stop at the first truthy

all(): stop at the first falsy

True or False

as soon as the answer is certain

  1. an iterable of values — [0, "", "hi"]
  2. any(): stop at the first truthy — all(): stop at the first falsy
  3. True or False — as soon as the answer is certain

Command

python
any(x > 0 for x in values)     # True if at least one value is positive
all(x > 0 for x in values)     # True only if every value is positive

any() is True on the first truthy item; all() is False on the first falsy one — both short-circuit, and any([]) is False while all([]) is True.

5.sum and len

len(obj) returns how many items a container or sequence holds. sum(iterable, start=0) adds every item to start, left to right — it works on numbers, not strings, because Python won't guess whether + should concatenate or add.

Key features

  • len(obj) works on any sequence or collection — list, str, tuple, dict, set
  • len() is O(1) — it reads a stored size, it does not walk the container
  • sum(iterable, start=0) adds every item to start, left to right
  • sum() raises TypeError on strings — start defaults to 0, and 0 + "a" is undefined
  • sum(nested, []) can flatten lists but it's O(n²) — str.join / itertools.chain are better

len() and sum() across types

len() and sum() across types
CallResult
len([1, 2, 3])3
len("hello")5 — counts characters, not bytes
len({"a": 1, "b": 2})2 — counts keys
sum([1, 2, 3])6
sum([1, 2, 3], 10)16 — start shifts the total
len() vs sum()

len(obj)

  • Reads a size every container already stores
  • O(1) — same cost for 3 items or 3 million
  • Works on list, str, tuple, dict, set

sum(iterable)

  • Walks the iterable, adding to start (default 0)
  • O(n) — genuinely visits every item
  • TypeError on strings — 0 + "a" is undefined
  • len(obj)
    • Reads a size every container already stores
    • O(1) — same cost for 3 items or 3 million
    • Works on list, str, tuple, dict, set
  • sum(iterable)
    • Walks the iterable, adding to start (default 0)
    • O(n) — genuinely visits every item
    • TypeError on strings — 0 + "a" is undefined

Command

python
len(container)              # item count — list, str, tuple, dict, set
sum(iterable)                # total, starting from 0
sum(iterable, start=100)     # total, starting from 100

len() is an O(1) lookup on any sequence or collection. sum() adds numbers left to right and raises TypeError on strings — use str.join instead.

6.map

map(func, iterable) applies func to every item, lazily, and yields the results one at a time. map(func, a, b) applies func to items from a and b in parallel, stopping at the shorter one — the same behavior zip() has for pairing.

Key features

  • map(func, iterable) applies func to every item, lazily — nothing runs until consumed
  • map(func, a, b, ...) applies func to items from several iterables in parallel
  • With multiple iterables, map stops at the shortest one, same as zip()
  • Returns a map object (an iterator) — wrap in list() to materialize
  • A list comprehension ([func(x) for x in it]) does the same job for one iterable

map() with one and several iterables

map() with one and several iterables
CallResult (materialized)
list(map(str.upper, ["a", "b"]))['A', 'B']
list(map(len, ["a", "bb", "ccc"]))[1, 2, 3]
list(map(lambda x, y: x + y, [1, 2], [10, 20]))[11, 22] — parallel across two iterables
list(map(lambda x, y: x + y, [1, 2, 3], [10, 20]))[11, 22] — stops at the shorter iterable
One function applied to every item, lazily

an iterable

[1, 2, 3]

map(func, ...) applies func

to each item, one at a time

a lazy map object

list() to materialize

  1. an iterable — [1, 2, 3]
  2. map(func, ...) applies func — to each item, one at a time
  3. a lazy map object — list() to materialize

Command

python
results = map(func, iterable)             # lazy — one func call per consumed item
results = map(func, iter_a, iter_b)       # parallel, stops at the shorter iterable
results = list(map(func, iterable))       # materialize if a real list is needed

map(func, iterable) is lazy — nothing runs until consumed — and with several iterables it pairs items positionally and stops at the shortest, same as zip().

7.range

range(stop), range(start, stop), and range(start, stop, step) describe an evenly-spaced sequence of integers without storing them — each value is computed only when asked for. stop is always excluded, the same half-open rule slicing uses.

Key features

  • range(stop) starts at 0; range(start, stop) starts at start — stop is always excluded
  • range(start, stop, step) skips by step, and step can be negative for a countdown
  • range is lazy — range(10**9) is created instantly, no matter how large stop is
  • range supports len(), indexing, and "in" — a real sequence, not a generator
  • list(range(...)) materializes it into an actual list, when one is genuinely needed

range() forms

range() forms
CallResult (materialized)
list(range(5))[0, 1, 2, 3, 4]
list(range(2, 5))[2, 3, 4]
list(range(0, 10, 2))[0, 2, 4, 6, 8]
list(range(5, 0, -1))[5, 4, 3, 2, 1]
len(range(3, 20, 4))5 — computed, not counted
A formula for a sequence, not a stored list

start, stop, step

range(0, 10, 2) — three ints stored

computed on demand

each value is arithmetic, not a lookup

0, 2, 4, 6, 8

stop (10) always excluded

  1. start, stop, step — range(0, 10, 2) — three ints stored
  2. computed on demand — each value is arithmetic, not a lookup
  3. 0, 2, 4, 6, 8 — stop (10) always excluded

Command

python
range(stop)                # 0, 1, ..., stop - 1
range(start, stop)         # start, ..., stop - 1
range(start, stop, step)   # start, start + step, ... — stop always excluded

range(start, stop, step) is a lazy, memory-cheap sequence — computed on demand, stop always excluded, and step can be negative to count down.

8.min and max

min(iterable) and max(iterable) return the smallest or largest item, comparing items directly unless key=func is given to compare by a derived value instead. Both also accept several separate arguments: min(a, b, c).

Key features

  • min(iterable) / max(iterable) return the smallest/largest ITEM, not its position
  • Also callable as min(a, b, c, ...) — several separate arguments, no iterable needed
  • key=func compares func(item) instead of item itself, same idea as sorted()
  • default=value avoids a ValueError on an empty iterable, returning value instead
  • On a tie, min()/max() return the FIRST item that achieves the extreme value

min() and max() variations

min() and max() variations
CallResult
max([3, 1, 4, 1, 5])5
min(3, 1, 4)1 — separate arguments, no list needed
max(["fig", "banana", "kiwi"], key=len)'banana' — longest string
min([], default=0)0 — avoids ValueError on empty input

Command

python
max(iterable)                 # largest item, direct comparison
max(iterable, key=len)        # largest item, by a derived value
max(a, b, c)                  # largest of several separate arguments
max(iterable, default=None)   # avoid ValueError on an empty iterable

min()/max() return the item itself, not its position. Both raise ValueError on an empty iterable unless default= is given.

9.iter and next

iter(iterable) returns an iterator — an object with its own next(). next(iterator) pulls the next value, raising StopIteration once exhausted. A for loop is exactly iter() once, then next() repeatedly, catching StopIteration to stop.

Key features

  • iter(iterable) returns a fresh, independent iterator, positioned at the start
  • next(iterator) returns the next value and advances the iterator by one
  • next() raises StopIteration once the iterator is exhausted
  • next(iterator, default) returns default instead of raising, once exhausted
  • A for loop is exactly iter() once, then next() in a loop, stopping on StopIteration

iter() and next() by hand

iter() and next() by hand
CallResult
it = iter([1, 2]); next(it)1
next(it) # same it, second call2
next(it) # same it, third callraises StopIteration
next(it, "done") # after exhaustion'done' — default suppresses the error
What a for loop does underneath

an iterable

[10, 20, 30]

iter() gets an iterator

a fresh position, once

next() pulls one value

repeatedly, until StopIteration

  1. an iterable — [10, 20, 30]
  2. iter() gets an iterator — a fresh position, once
  3. next() pulls one value — repeatedly, until StopIteration

Command

python
it = iter(sequence)          # get an iterator, once
value = next(it)              # pull one value, advances the iterator
value = next(it, default)     # pull one value, or default if exhausted

iter(obj) returns a fresh iterator; next(it) pulls one value, raising StopIteration when exhausted. A for loop is exactly this pair, automated.

10.isinstance and issubclass

isinstance(obj, type) checks whether obj is an instance of type — or of any type in a tuple of types — including subclasses. issubclass(cls, type) asks the same question about a class itself, not an instance of it.

Key features

  • isinstance(obj, type) is True if obj is an instance of type, or any subclass of it
  • isinstance(obj, (T1, T2)) checks against several types at once — True if any match
  • issubclass(cls, type) checks a CLASS, not an instance — issubclass(bool, int) is True
  • Preferred over type(obj) == type for checks, because it also matches subclasses
  • isinstance(True, int) is True — bool is a subclass of int, not a separate, unrelated type

isinstance() and issubclass() across types

isinstance() and issubclass() across types
CallResult
isinstance(5, int)True
isinstance(5, (int, float))True — matches any type in the tuple
isinstance(True, int)True — bool is a subclass of int
issubclass(bool, int)True
issubclass(int, bool)False — not the other way around

Command

python
isinstance(obj, SomeType)              # is obj an instance of SomeType (or a subclass)?
isinstance(obj, (TypeA, TypeB))        # is obj an instance of any of these?
issubclass(SomeClass, SomeType)        # does SomeClass inherit from SomeType?

isinstance checks an object (including subclasses); issubclass checks a class. bool is a subclass of int, so isinstance(True, int) is True.

11.hasattr, getattr, and setattr

getattr(obj, name, default) reads an attribute by its name as a string, returning default instead of raising if it's missing. setattr(obj, name, value) writes one the same way. hasattr(obj, name) checks existence first, without reading.

Key features

  • getattr(obj, name) reads obj.name where name is a STRING — raises if missing
  • getattr(obj, name, default) returns default instead of raising, if name is missing
  • setattr(obj, name, value) is the same as obj.name = value, with name as a string
  • hasattr(obj, name) returns True/False — calls getattr internally, catches the exception
  • getattr(obj, name) with a computed 'name' is the main reason to prefer it over obj.attr

hasattr / getattr / setattr on a simple object

hasattr / getattr / setattr on a simple object
CallResult
getattr(config, "timeout")30 — same as config.timeout, if it exists
getattr(config, "retries", 3)3 — default used, no AttributeError, if missing
hasattr(config, "timeout")True
setattr(config, "timeout", 60)None — but config.timeout is now 60

Command

python
getattr(obj, "name")             # same as obj.name — raises if missing
getattr(obj, "name", default)    # same, but returns default instead of raising
setattr(obj, "name", value)      # same as obj.name = value
hasattr(obj, "name")             # True/False, no AttributeError either way

getattr/setattr/hasattr work by NAME as a string — the only way to look up an attribute chosen at runtime.

12.callable

callable(obj) returns True if obj can be called with (), without actually calling it. Functions, classes, and methods are callable; most everyday values — an int, a str, a list — are not, unless their type defines __call__.

Key features

  • callable(obj) is True for functions, classes, and methods
  • callable(obj) is False for an int, str, list, dict — ordinary data has no __call__
  • A class is always callable — calling it is how an instance gets created
  • An instance is callable only if its class defines __call__
  • callable() never calls obj — it only checks whether () would be legal

callable() across common objects

callable() across common objects
Objectcallable()?
a plain function, len, printTrue
a class, e.g. list, dict, or a user-defined oneTrue — calling it constructs an instance
5, "hi", [1, 2, 3]False — plain data, nothing to call
an instance whose class defines __call__True

Command

python
callable(obj)   # True if obj() would be legal, without actually calling it

callable(obj) checks whether obj() would be legal, without running it — True for functions/classes always, for an instance only if __call__ is defined.

Remember

  • zip() and map() (multi-iterable) both stop at the shortest input — silently, no error.
  • any([]) is False; all([]) is True — the classic vacuous-truth trap on an empty iterable.
  • sorted()/reversed() return something new; list.sort()/list.reverse() mutate in place and return None.
  • map(), filter(), and a plain generator are all one-shot — consume them twice and the second pass sees nothing.

Pro tip

  • getattr(obj, name, default) is the only way to look up an attribute whose name is a runtime variable.
  • isinstance(x, int) is True for a bool too — check bool first if that distinction matters.
  • callable(obj) only checks whether obj() is legal — it never checks whether the call would succeed.
  • max()/min() take key= exactly like sorted() does, to compare by a derived value instead of the items.

Next up

  • Modules and imports
  • Object-Oriented Python
  • Dunder methods

Sources

  • Built-in functions — docs.python.org/3/library/functions.html
  • Every example executed on Python 3.14.3 · checked 2026-08-20

Spotted a mistake on this page? Tell us.