Guides Guide 7 Reference

Built-in Functions

71 callable built-in names in this reference. Grouped by the job they do, with every parameter explained and several ways to call each one.

These names live in the builtins module, which Python loads into every scope before your first line runs. Normal Python code needs no import for these names. A local name can hide a built-in. This page is a working reference. Each entry gives the signature, what each parameter means, three or four real call forms, and the trap that catches people.

Some of these are classes, not functions. int, list, str, type and friends are types. You call them and get an instance. The docs list them here because you use them exactly like functions. A trailing / in a signature means the parameters before it are positional only. You cannot pass them by name.

Contents

  1. The 20 you use daily
  2. Math and numbers (7)
  3. Number types and bases (7)
  4. Text and characters (8)
  5. Build containers (10)
  6. Loop and reshape data (13)
  7. Inspect objects (13)
  8. Write classes (4)
  9. See the current scope (2)
  10. Run code from text (4)
  11. Files (1)
  12. Debug and help (2)
  13. Pairs people confuse
  14. Looks built-in, is not

The 20 you use daily

If you learn nothing else, learn these. They cover almost every line of real Python.

lenrangeenumeratezip sortedsumminmax intstrfloatlist dictsettupleprint isinstanceanyallopen
The one habit that matters. Many built-ins use fast C code in CPython. User callbacks still run as Python code. sum(xs), max(xs) and any(...) often help readability. Speed depends on the runtime and workload. Measure the code you use. Reach for the built-in first.

Run the examples

Each code block has its own data. Copy one block into a Python file and run it.

Use Python 3.10 or newer. Output comments show results from a terminal session.

A bare expression in a script does not print. Add print(...) to see its result.

Download all 71 examples. Run python3 builtin_examples.py.

File examples use a private temp folder. The input demo uses sample answers.

The async examples define helpers. The debugger example defines a function but does not pause this run.

Math and numbers

Seven functions. Arithmetic that is common enough to not need import math.

abs(x, /)

Distance from zero. Drops the minus sign.

xs = [-5, 2, -1]
abs(-5)            # 5
abs(-2.5)          # 2.5
abs(3 + 4j)        # 5.0   the magnitude, not a part
sorted(xs, key=abs)  # sort by size, ignoring sign

Trap. abs(-2**63) is fine in Python. Ints have no fixed width, so there is no overflow.

divmod(a, b, /)

Floor division and remainder in one call. Returns a 2-tuple.

divmod(7, 2)             # (3, 1)      same as (7 // 2, 7 % 2)
divmod(-7, 2)            # (-4, 1)     floor, so it rounds down
hours, mins = divmod(135, 60)   # (2, 15)
divmod(7.5, 2)           # (3.0, 1.5)  floats work too

Use it for. Time splits, base conversion, and grid coordinates from a flat index.

max(iterable, *, key=None, default) or max(a, b, *args, key=None)

The largest item. Two call forms: one iterable, or several separate values.

from types import SimpleNamespace
words = ['bee', 'honey', 'a']
people = [SimpleNamespace(age=20), SimpleNamespace(age=30)]
counts = {'bee': 3, 'honey': 5}
max([3, 9, 1])                       # 9
max(3, 9, 1)                         # 9      separate args
max(words, key=len)                  # longest word
max(people, key=lambda p: p.age)     # oldest person object
max([], default=0)                   # 0      no exception
max(counts, key=counts.get)          # key with the biggest value

Trap. key and default are keyword only. max(xs, len) is a silent bug: it compares the list to the function.

min(iterable, *, key=None, default) or min(a, b, *args, key=None)

The smallest item. Same rules as max in every way.

a, b = 7, 4
points = [(3, 4), (1, 1)]
intervals = [(1, 5), (2, 3)]
min([3, 9, 1])                       # 1
min(a, b)                            # 4, the smaller value
min(points, key=lambda p: p[0]**2 + p[1]**2)   # closest to origin
min(intervals, key=lambda iv: iv[1]) # earliest end time

Idiom. max(low, min(high, x)) clamps x into a range.

pow(base, exp, mod=None)

Raise to a power. With three arguments it does fast modular exponentiation.

pow(2, 10)             # 1024        same as 2 ** 10
pow(2, -1)             # 0.5         negative exponent gives a float
pow(2, 100, 1000)      # 376         fast, never builds the huge number
pow(3, -1, 7)          # 5           modular inverse, Python 3.8+

Why three args. 2**100 % 1000 builds a 31-digit number first. pow(2, 100, 1000) never does. This is the crypto and hashing path.

round(number, ndigits=None)

Round to a given number of decimal places.

round(3.567)           # 4      an int, not a float
round(3.567, 2)        # 3.57
round(1234, -2)        # 1200   round to the nearest hundred
round(2.5)             # 2      banker's rounding, see below
round(0.5)             # 0

Trap. Python rounds halves to the even number. So round(2.5) is 2 and round(3.5) is 4. This is deliberate and reduces bias. For money, use decimal.Decimal with ROUND_HALF_UP.

sum(iterable, /, start=0)

Add up the items, left to right.

nums = [-2, 0, 3]
sum([1, 2, 3])              # 6
sum([1, 2, 3], 100)         # 106
sum(x*x for x in nums)      # generator, no temp list
sum(1 for x in nums if x > 0)   # count matches
sum([[1], [2]], [])         # [1, 2]  works, but O(n^2). Use itertools.chain

Trap. sum refuses strings on purpose. Use "".join(parts). For floats needing precision, use math.fsum.

Number types and bases

Seven functions. Convert between number types, and between an int and its text form in another base.

int(x=0) or int(x, base=10)

A whole number. Truncates floats toward zero, or parses text.

int('42')            # 42
int(3.99)            # 3       truncates, never rounds
int(-3.99)           # -3      toward zero, not down
int('ff', 16)        # 255
int('1010', 2)       # 10
int('0x1f', 0)       # 31      base guessed from the prefix
int('  7 ')          # 7       surrounding spaces are fine

Trap. int('3.5') raises ValueError. Go through float first: int(float('3.5')).

float(x=0.0, /)

A 64-bit decimal number.

float('3.5')         # 3.5
float(7)             # 7.0
float('1e-3')        # 0.001
float('inf')         # inf     handy as a starting "worst" value
float('nan') == float('nan')   # False. Use math.isnan

Trap. 0.1 + 0.2 != 0.3. That is binary floating point, not a Python bug. Compare with math.isclose.

complex(real=0, imag=0) or complex(string)

A number with a real and an imaginary part.

complex(1, 2)        # (1+2j)
complex('1+2j')      # (1+2j)   note: no spaces allowed inside
z = 3 + 4j
z.real, z.imag       # (3.0, 4.0)
abs(z)               # 5.0

Handy for. 2D grid problems. One complex number holds x and y, and adding does the move.

bool(x=False, /)

True or False. Applies Python's truthiness rules.

flags = [True, False, True]
bool([])             # False
bool([0])            # True    non-empty list, contents ignored
bool('False')        # True    any non-empty string is true
bool(0.0)            # False
True + True          # 2       bool is a subclass of int
sum(flags)           # counts the True values

Style. Write if items: not if len(items) > 0:. Both work; the first is idiomatic.

bin(x, /)

The binary text form of an int, with a 0b prefix.

x = 13
bin(10)                    # '0b1010'
bin(10)[2:]                # '1010'    drop the prefix
format(10, '08b')          # '00001010' padded, no prefix
bin(-5)                    # '-0b101'  sign, not two's complement
bin(x).count('1')          # popcount. Or x.bit_count() in 3.10+

hex(x, /)

The hexadecimal text form of an int, with a 0x prefix.

hex(255)                   # '0xff'
hex(255)[2:]               # 'ff'
format(255, '04X')         # '00FF'   padded and upper case
int(hex(255), 16)          # 255      round trip
bytes([255, 0]).hex()      # 'ff00'   for byte strings

oct(x, /)

The octal text form of an int, with a 0o prefix.

oct(8)                 # '0o10'
oct(0o755)             # '0o755'
int('755', 8)          # 493
mode = 0o644           # Example Unix permission bits. No file changes.

Text and characters

Eight functions. Make text, show text, and move between a character and its code point.

str(object='') or str(bytes, encoding, errors='strict')

Text. The friendly form of a value, meant for humans.

value = 42
str(42)                          # '42'
str([1, 2])                      # '[1, 2]'
str(b'hi', 'utf-8')              # 'hi'   same as b'hi'.decode()
str(b'\xff', 'utf-8', 'replace') # a placeholder char, no crash
f'{value}'                       # calls str() under the hood

repr(obj, /)

The unambiguous form of a value, meant for developers. Should look like valid Python.

name = 'Balbir'
repr('a')            # "'a'"    quotes are visible
str('a')             # 'a'      quotes are not
repr('a\nb')         # "'a\\nb'"  escapes show up
print(f'{name!r}')   # !r means "use repr here"

Use it when. Debugging whitespace or type confusion. repr shows the difference between 1 and '1'; str hides it.

ascii(obj, /)

Like repr, but escapes every non-ASCII character.

ascii('café')        # "'caf\\xe9'"
repr('café')         # "'café'"
ascii('日本')         # "'\\u65e5\\u672c'"

Use it when. A log file or terminal cannot handle Unicode, or you need to see exactly which code points are in a string.

format(value, format_spec='')

Apply one format spec to one value. The engine behind f-strings.

format(3.14159, '.2f')     # '3.14'
format(1234567, ',')       # '1,234,567'
format(0.256, '.1%')       # '25.6%'
format('hi', '>10')        # '        hi'  right aligned
format(255, '#06x')        # '0x00ff'
f'{3.14159:.2f}'           # same thing, preferred in normal code

When to call it directly. When the spec itself is a variable: format(x, spec).

chr(i, /)

The character for a Unicode code point.

chr(65)                          # 'A'
chr(97)                          # 'a'
chr(ord('a') + 1)                # 'b'   next letter
[chr(ord('a') + i) for i in range(26)]   # the alphabet

ord(c, /)

The code point for one character. The inverse of chr.

counts = [0] * 26
ch = 'b'
ord('A')                   # 65
ord('a') - ord('A')        # 32    case gap
counts[ord(ch) - ord('a')] += 1    # 26-slot array instead of a dict

Trap. ord('ab') raises TypeError. One character only.

print(*objects, sep=' ', end='\n', file=None, flush=False)

Write values to a stream, with separators.

import sys
my_list = ['bee', 'honey']
pct = 50
print('a', 'b')                        # a b
print('a', 'b', sep='-')               # a-b
print('loading', end='')               # no newline
print(*my_list, sep='\n')              # one item per line
print('error', file=sys.stderr)        # to the error stream
print(f'{pct}%', end='\r', flush=True) # a live-updating line

input(prompt='')

Read one line from standard input. Always returns a string.

def read_person(ask=input):
    name = ask('Name: ')
    age = int(ask('Age: '))
    return name, age

answers = iter(['Balbir', '30'])
print(read_person(lambda prompt: next(answers)))  # ('Balbir', 30)
# Use read_person() in a terminal to enter real answers.
n, m = map(int, '3 5'.split())

Trap. The trailing newline is stripped, but other whitespace is not. And input() raises EOFError at end of file, so guard it in scripts that read piped input.

Build containers

Ten functions. Every core data structure, plus the two lazy ones.

list(iterable=(), /)

An ordered sequence you can change.

xs, ys = [1, 2], ['a', 'b']
original = [[1], [2]]
list()                     # []
list('abc')                # ['a', 'b', 'c']
list(range(3))             # [0, 1, 2]
list({'a': 1})             # ['a']   dicts iterate over keys
list(zip(xs, ys))          # drain an iterator into memory
copy = list(original)      # a shallow copy

Trap. list(gen) drains the generator. A second call gives [].

tuple(iterable=(), /)

An ordered sequence you cannot change. Hashable if its items are.

seen = set()
row = [1, 2]
cache = {}
x, y, value = 1, 2, 'bee'
tuple([1, 2])              # (1, 2)
tuple('ab')                # ('a', 'b')
(1,)                       # a 1-tuple. The comma makes it, not the brackets
seen.add(tuple(row))       # lists cannot go in a set. Tuples can
cache[(x, y)] = value      # tuples make fine dict keys

dict(**kwargs) or dict(mapping) or dict(pairs)

A key to value map. Keeps insertion order since Python 3.7.

keys, values = ['a', 'b'], [1, 2]
base, overrides = {'a': 1}, {'a': 9}
dict(a=1, b=2)                   # {'a': 1, 'b': 2}
dict([('a', 1), ('b', 2)])       # same
dict(zip(keys, values))          # the pairing idiom
dict.fromkeys('abc', 0)          # {'a': 0, 'b': 0, 'c': 0}
{**base, **overrides}            # merge, right side wins

Trap. Keys must be hashable, so no lists. And dict(a=1) cannot make a key with a space or a dash in it.

set(iterable=(), /)

An unordered bag of unique, hashable items. O(1) membership tests.

a, b = {1, 2}, {2, 3}
xs = [1, 1, 2]
set([1, 1, 2])             # {1, 2}
set()                      # the empty set. {} is an empty DICT
set('hello')               # {'h', 'e', 'l', 'o'}
a | b, a & b, a - b        # union, intersection, difference
len(set(xs)) != len(xs)    # "has duplicates" in one line

Why it matters. Swapping x in a_list for x in a_set is the single most common way to turn O(n²) into O(n).

frozenset(iterable=(), /)

A set you cannot change. Because it is immutable, it is hashable.

seen = set()
members = ['a', 'b']
frozenset([1, 1, 2])                 # frozenset({1, 2})
groups = {frozenset({'a','b'}): 1}   # a set used as a dict key
seen.add(frozenset(members))         # dedupe unordered groups

Use it for. Anagram grouping, graph edge sets, and any cache key where order should not matter.

bytes(source=b'') or bytes(str, encoding, errors)

A fixed sequence of integers from 0 to 255. Raw binary data.

bytes([65, 66])            # b'AB'
bytes(3)                   # b'\x00\x00\x00'
bytes('hi', 'utf-8')       # b'hi'   same as 'hi'.encode()
b'AB'[0]                   # 65      indexing gives an INT
b'AB'[0:1]                 # b'A'    slicing gives bytes

Trap. Indexing a bytes object gives an int, not a one-byte string. Slice instead if you want bytes back.

bytearray(source=b'') or bytearray(str, encoding, errors)

The same as bytes, but you can change it in place.

buf = bytearray(b'hi')
buf[0] = 72                # bytearray(b'Hi')
buf.extend(b' there')      # grows in place, no copy
buf += b'!'
bytes(buf)                 # freeze it back

Use it for. Building a binary message piece by piece. Repeated bytes + bytes is O(n²); a bytearray is amortised O(1).

memoryview(object)

A window onto another object's bytes. Slicing it copies nothing.

mv = memoryview(b'abcdef')
mv[0]                      # 97
bytes(mv[1:3])             # b'bc'   the slice itself was free
data = bytearray(1_000_000)
chunk = memoryview(data)[500:600]   # no 1 MB copy
chunk[0] = 65              # writes straight into `data`

Use it for. Parsing large buffers without copying. data[500:600] on a bytearray copies; the memoryview version does not.

range(stop) or range(start, stop, step=1)

A lazy sequence of evenly spaced ints. Stores only three numbers.

range(3)                   # 0, 1, 2
range(2, 5)                # 2, 3, 4
range(0, 10, 2)            # 0, 2, 4, 6, 8
range(5, 0, -1)            # 5, 4, 3, 2, 1
len(range(10**18))         # instant. Nothing is built
10**17 in range(10**18)    # also instant. It does the maths

Not a generator. A range is re-iterable, supports len, indexing and slicing, and never runs out. It is a real sequence.

slice(stop) or slice(start, stop, step=None)

A reusable slice object. What a[1:5:2] builds behind the scenes.

record = 'Balbir    Singh'
s = slice(None, None, 2)
seq = [1, 2, 3, 4]
FIRST_NAME = slice(0, 10)        # name the columns once
record[FIRST_NAME]               # readable at every use site
[1,2,3,4][slice(None, None, 2)]  # [1, 3]   same as [::2]
s.indices(len(seq))              # (start, stop, step) clamped to the length

Use it for. Fixed-width record parsing, and inside __getitem__ when you handle both ints and slices.

Loop and reshape data

Thirteen functions. The largest group, and the one that shapes how Python code reads.

len(s, /)

How many items. O(1) for every built-in type.

len('hello')       # 5
len({'a': 1})      # 1
len(range(10**9))  # 1000000000, no list is built
try:
    len(x for x in [1, 2])
except TypeError:
    print('Generators have no length')

Trap. No length on generators, files or map objects. Convert with list() first, or use sum(1 for _ in it).

iter(object) or iter(callable, sentinel)

Get an iterator. The second form calls a function until it returns a stop value.

from io import BytesIO
it = iter([1, 2, 3])
print(next(it))                       # 1
f = BytesIO(b'bee honey')
chunks = []
for chunk in iter(lambda: f.read(4), b''):
    chunks.append(chunk)
print(chunks)                         # [b'bee ', b'hone', b'y']
answers = iter(['bee', 'honey', 'quit'])
for line in iter(lambda: next(answers), 'quit'):
    print(line)

The pairing idiom. it = iter(xs); list(zip(it, it)) chunks a flat list into pairs.

aiter(async_iterable, /)

The async version of iter. Python 3.10+.

handle = print
async def consume(stream):
    ait = aiter(stream)        # what `async for` calls for you
    async for item in stream:  # the normal way to consume one
        handle(item)

anext(async_iterator, default)

The async version of next. Returns an awaitable. Python 3.10+.

async def peek(ait):
    first = await anext(ait)         # raises StopAsyncIteration when empty
    safe = await anext(ait, None)    # None instead of an error
    return first, safe

Trap. You must await it. Forgetting gives you a coroutine object, not a value.

enumerate(iterable, start=0)

Pair each item with a running count. Lazy.

s = 'ab'
pairs = [(1, 2), (3, 4)]
list(enumerate('ab'))            # [(0, 'a'), (1, 'b')]
list(enumerate('ab', 1))         # [(1, 'a'), (2, 'b')]
for i, ch in enumerate(s):
    print(i, ch)
for i, (a, b) in enumerate(pairs):   # nested unpacking works
    print(i, a, b)

Style. Never write for i in range(len(xs)). Use enumerate. Reviewers notice.

zip(*iterables, strict=False)

Walk several iterables together, yielding tuples. Lazy.

xs, ys = [1, 2], ['a', 'b']
keys, values = ['a', 'b'], [1, 2]
matrix = [[1, 2], [3, 4]]
list(zip([1,2], 'ab'))               # [(1,'a'), (2,'b')]
list(zip([1,2,3], 'ab'))             # [(1,'a'), (2,'b')]  silently short
list(zip(xs, ys, strict=True))       # raises on a length mismatch
dict(zip(keys, values))              # build a dict from two lists
list(zip(*matrix))                   # transpose rows and columns
zip(xs, xs[1:])                      # every neighbouring pair

Trap. Without strict=True, zip stops at the shortest input. That hides bugs. Use itertools.zip_longest when you want padding instead.

map(function, *iterables)

Apply a function to every item. Lazy: nothing runs until you consume it.

list(map(str, [1, 2]))              # ['1', '2']
n, m = map(int, '3 5'.split())       # 3, 5
list(map(pow, [2, 3], [3, 2]))       # [8, 9]
words = ['bee', 'honey']
max(map(len, words))                # 5

Style. map(f, xs) beats a comprehension only when f already exists. map(lambda x: x*2, xs) is slower and uglier than [x*2 for x in xs].

filter(function, iterable)

Keep items where the test is true. Lazy.

chars = ['1', 'a', '2']
nums = [-1, 0, 2]
pred = lambda x: x > 0
xs = [-1, 2]
list(filter(None, [0, 1, '', 2]))        # [1, 2]  drops falsy values
list(filter(str.isdigit, chars))         # only digits
list(filter(lambda x: x > 0, nums))      # prefer [x for x in nums if x > 0]
next(filter(pred, xs), None)             # find first match, lazily

Worth knowing. filter(None, xs) is the compact way to strip empties. Everything else reads better as a comprehension.

sorted(iterable, /, *, key=None, reverse=False)

A new sorted list. Timsort: O(n log n), stable, and O(n) on nearly sorted input.

from types import SimpleNamespace
words = ['Bee', 'honey', 'a']
people = [SimpleNamespace(age=20, name='A'), SimpleNamespace(age=30, name='B')]
counts = {'bee': 3, 'honey': 5}
d = counts
sorted([3, 1])                             # [1, 3]
sorted(words, key=len)                     # by length
sorted(words, key=str.lower)               # case insensitive
sorted(people, key=lambda p: (-p.age, p.name))   # age desc, then name asc
sorted(counts.items(), key=lambda kv: kv[1], reverse=True)
sorted(d, key=d.get)                       # keys, ordered by value

Stable means. Equal items keep their original order. So you can sort twice: least important key first, most important key last.

reversed(seq, /)

A lazy iterator walking a sequence backwards.

n = 3
s = 'bee'
my_dict = {'a': 1, 'b': 2}
list(reversed([1, 2]))           # [2, 1]
for i in reversed(range(n)):     # count down, no list built
    print(i)
''.join(reversed(s))             # same as s[::-1], but lazy
reversed(my_dict)                # keys, newest first. Python 3.8+

Trap. Generators and set have no order, so reversed rejects them. xs[::-1] copies; reversed(xs) does not.

all(iterable, /)

True when every item is truthy. Stops at the first false one.

nums = [1, 2, 3]
xs = [1, 'a']
all([1, 2, 0])                   # False
all([])                          # True   "no counter-example found"
all(x > 0 for x in nums)         # short-circuits on the first failure
all(isinstance(x, int) for x in xs)

Trap. Pass a generator, not a list comprehension. all([f(x) for x in xs]) evaluates every item before checking any of them.

any(iterable, /)

True when at least one item is truthy. Stops at the first true one.

banned = ['spam', 'junk']
text = 'bee honey'
errors = [False, False]
ship = lambda: print('No errors')
any([0, 0, 3])                   # True
any([])                          # False
any(word in text for word in banned)     # exits early on a hit
if not any(errors): ship()       # "none of them" reads as `not any`

Inspect objects

Thirteen functions. Ask an object what it is and what it holds.

type(object) or type(name, bases, dict)

One argument reads a class. Three arguments create one.

x = 3
type(1)                          # <class 'int'>
type(x).__name__                 # 'int'   just the name
type(x) is list                  # exact type, no subclasses
Point = type('Point', (), {'x': 0})   # a class made at runtime

Style. For a type check, use isinstance. Use type(x) is C only when a subclass must not pass.

isinstance(object, classinfo, /)

Is this value of that type, or a subclass of it?

import collections.abc
x = [1, 2]
isinstance(1, int)                     # True
isinstance(True, int)                  # True. bool subclasses int
isinstance(x, (int, float))            # either one
isinstance(x, int | str)               # 3.10+ syntax
isinstance(x, collections.abc.Iterable)   # "does it behave like one"

Trap. isinstance(True, int) is True. If you must reject booleans, test type(x) is int.

issubclass(class, classinfo, /)

Does one class inherit from another? Both arguments must be classes.

issubclass(bool, int)    # True
issubclass(int, int)     # True
issubclass(int, object)  # True
try:
    issubclass(1, int)
except TypeError:
    print('The first argument must be a class')

callable(object, /)

Can you put brackets after it? True for functions, classes and objects with __call__.

callable(len)                    # True
callable(int)                    # True. Classes are callable
callable(42)                     # False
callable(lambda: 1)              # True

Note. True is not a guarantee. Calling it can still raise TypeError on the arguments.

getattr(object, name, default)

Read an attribute whose name is a string.

from types import SimpleNamespace
obj = SimpleNamespace(x=3)
field_name = 'x'
self = SimpleNamespace(do_save=lambda: 'saved', unknown=lambda: 'unknown')
cmd = 'save'
getattr(obj, 'x')                # same as obj.x
getattr(obj, 'x', 0)             # 0 when missing
getattr(obj, field_name)         # the whole point: a dynamic name
handler = getattr(self, f'do_{cmd}', self.unknown)   # dispatch table

Use it for. Command dispatch and config-driven code. Avoid it when the name is a literal; obj.x is clearer and faster.

setattr(object, name, value, /)

Write an attribute whose name is a string.

from types import SimpleNamespace
obj = SimpleNamespace(x=3)
self = obj
config = {'x': 5, 'y': 2}
setattr(obj, 'x', 5)             # same as obj.x = 5
for k, v in config.items():
    setattr(self, k, v)          # bulk-assign from a dict

Trap. Fails on classes using __slots__ if the name is not declared there.

delattr(object, name, /)

Remove an attribute whose name is a string.

from types import SimpleNamespace
obj = SimpleNamespace(cache={}, tmp=1)
delattr(obj, 'cache')            # same as del obj.cache
if hasattr(obj, 'tmp'):
    delattr(obj, 'tmp')          # guard first, or catch AttributeError

hasattr(object, name, /)

Does this attribute exist? Implemented as a getattr with the error caught.

from io import StringIO
obj = StringIO('bee')
hasattr([], 'append')            # True
hasattr(obj, 'read')             # duck typing: "is it file-like"

Trap. If the attribute is a property that raises, hasattr reports False. Prefer getattr(obj, name, sentinel) when that matters.

dir(object)

A sorted list of the names an object exposes. No argument gives the current scope.

x = 3
obj = []
dir()                                   # names defined right here
dir(str)                                # every string method
[m for m in dir(x) if not m.startswith('_')]   # the public surface
'append' in dir(obj)

Note. This is for humans exploring in a REPL. It can be customised by __dir__, so do not build logic on it.

vars(object)

The __dict__ of an object. No argument acts like locals().

import json
from types import SimpleNamespace
obj = SimpleNamespace(x=5)
vars(obj)                        # {'x': 5}
vars()                           # same as locals()
json.dumps(vars(obj))            # quick serialise of a simple object
vars(obj)['x'] = 9               # writes through. It is the real dict

Trap. Raises TypeError on objects with __slots__, and on ints and strings.

id(object, /)

A unique int for the object, for as long as it lives. In CPython, its memory address.

x = []
y = x
id(x) == id(y)                   # same object. Prefer `x is y`
a = [1]; b = a
id(a) == id(b)                   # True. Two names, one list

Trap. Ids are reused after an object is freed. Never store one as a long-lived key.

hash(object, /)

An int fingerprint. Equal objects must hash equal. Used by dicts and sets.

hash('a')               # Process-specific integer, not a durable ID.
hash(1) == hash(1.0)     # True
try:
    hash([1])
except TypeError:
    print('Lists are unhashable')

Note. String hashes are randomised per process for security. Never persist one to disk. Use hashlib for a stable digest.

object()

The root of every class. Calling it gives a bare instance with no attributes.

MISSING = object()               # a unique sentinel value
def f(x=MISSING):
    if x is MISSING: ...         # distinguishes "not passed" from None
class C(object): ...             # Python 2 habit. Just write class C:

The real use. A sentinel. Nothing else in the program can ever equal it, so None stays free to be a valid value.

Write classes

Four functions. Three are used as decorators; the fourth reaches upward.

@classmethod

A method that receives the class, not the instance. Call it on the class or an instance.

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    @classmethod
    def from_string(cls, text):        # an alternative constructor
        x, y = map(int, text.split(','))
        return cls(x, y)               # cls, so subclasses work too

Point.from_string('1,2')

Main use. Factory methods. Using cls(...) instead of Point(...) means a subclass gets its own type back.

@staticmethod

A plain function that lives inside a class. Gets neither self nor cls.

class Temp:
    @staticmethod
    def c_to_f(c):                     # no self needed
        return c * 9 / 5 + 32

Temp.c_to_f(100)                       # 212
Temp().c_to_f(100)                     # works on an instance too

When to use. A helper that belongs with the class for readability but touches no state. Since 3.10 these are callable as plain functions too.

@property or property(fget=None, fset=None, fdel=None, doc=None)

Make a method look like an attribute. Lets you add validation without changing callers.

class Circle:
    def __init__(self, r):
        self.r = r

    @property
    def area(self):                    # read as circle.area, no brackets
        return 3.14159 * self._r ** 2

    @property
    def r(self):
        return self._r

    @r.setter
    def r(self, value):                # validation on write
        if value < 0:
            raise ValueError('radius must be non-negative')
        self._r = value

Related. functools.cached_property computes once and stores the result on the instance.

super() or super(type, object_or_type)

A proxy to the next class in the method resolution order. Usually the parent.

class Base:
    def greet(self): return 'hi'

class Child(Base):
    def __init__(self, extra):
        super().__init__()             # run the parent's setup
        self.extra = extra

    def greet(self):
        return super().greet() + '!'   # extend, do not replace

Child.__mro__                          # the exact search order

Trap. "Next in the MRO" is not always the direct parent. With multiple inheritance it can be a sibling class. That is the feature, not a bug.

See the current scope

Two functions. Useful for debugging and templating, risky for logic.

globals()

The module-level namespace, as a real dict. Writing to it creates a global.

name = 'save'
def handler_save(): return 'saved'
globals()['handler_save'] = handler_save
globals()['__name__']            # '__main__'
globals()['dynamic'] = 5         # really does create a global
fn = globals()['handler_' + name]    # look up a function by name

Better option. A plain dict of handlers is clearer than name lookups in globals(), and a linter can check it.

locals()

The local namespace as a dict. At module level it is the same as globals().

name = 'Balbir'
def f(a, b):
    c = a + b
    print(locals())              # {'a': 1, 'b': 2, 'c': 3}
'Hello {name}'.format(**locals())    # quick templating

Trap. Inside a function this is a snapshot copy. Assigning into it does not change the variable. Python 3.13 makes that explicit.

Run code from text

Four functions. Powerful, and the usual source of remote code execution bugs.

Read this before using any of the four. Never pass user input to eval, exec or compile. There is no safe sandbox; passing empty globals does not help. For data, use json.loads. For a literal Python value, use ast.literal_eval, which handles only strings, numbers, tuples, lists, dicts, sets and booleans.

eval(expression, globals=None, locals=None)

Run one expression and return its value. Statements are not allowed.

import ast
eval('2 + 3')                    # 5
eval('x * 2', {'x': 4})          # 8
try:
    eval('x = 1')
except SyntaxError:
    print('eval needs an expression')
ast.literal_eval('[1, 2]')       # the safe choice for data

exec(object, globals=None, locals=None)

Run a block of statements. Always returns None.

ns = {}
exec('def double(x): return x * 2', ns)
ns['double'](5)                  # 10

Legitimate use. Code generation inside libraries. dataclasses and namedtuple both build methods with exec.

compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)

Turn source text into a code object you can run more than once.

import ast
src = 'x = 1'
code = compile('x + 1', '<expr>', 'eval')
eval(code, {'x': 1})             # 2. Reuse the code, skip re-parsing
tree = compile(src, 'f.py', 'exec', ast.PyCF_ONLY_AST)   # get the AST

Use it for. Static analysis via the AST, or a template engine that compiles once and runs many times.

__import__(name, globals=None, locals=None, fromlist=(), level=0)

The low-level hook behind the import statement.

import importlib
__import__('math').pi                    # 3.14159...
__import__('os.path')                    # returns `os`, not `os.path`
__import__('os.path', fromlist=['path']) # returns os.path
importlib.import_module('os.path')       # do this instead

Do not use it. The return value rule is confusing. importlib.import_module does the same job correctly.

Files

One function, and it carries eight parameters.

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

Open a file and return a file object.

from pathlib import Path
from tempfile import TemporaryDirectory

# This example owns its files. It never overwrites your notes.
with TemporaryDirectory(prefix='builtin-demo-') as folder:
    root = Path(folder)
    with open(root / 'note.txt', 'w', encoding='utf-8') as f:
        f.write('bee\nhoney\n')
    with open(root / 'note.txt', encoding='utf-8') as f:
        for line in f:
            print(line.rstrip())
    with open(root / 'note.txt', 'a', encoding='utf-8') as f:
        f.write('done\n')
    with open(root / 'data.bin', 'wb') as f:
        f.write(b'AB')
    with open(root / 'data.bin', 'rb') as f:
        print(f.read())               # b'AB'

Always use with. It closes the file even when an exception fires. And 'w' wipes an existing file the moment it opens, before you write a byte.

Debug and help

Two functions. Both are for you, not for production code.

help(request)

Print the docs for an object. No argument starts an interactive help session.

def double(x):
    '''Return twice the value.'''
    return x * 2

help(len)
help(double)
# Run help('modules') yourself to list local installed modules.

Related. obj.__doc__ gets the raw docstring string, without the formatting.

breakpoint(*args, **kws)

Drop into the debugger at this line. Python 3.7+.

def f(x):
    breakpoint()                 # pdb opens here
    return x * 2

Two things worth knowing. Set PYTHONBREAKPOINT=0 to disable every breakpoint without editing code. Set PYTHONBREAKPOINT=ipdb.set_trace to swap in a nicer debugger.

Pairs people confuse

PairThe difference
str vs reprstr is for users, repr is for developers. repr shows the quotes.
sorted vs .sort()sorted returns a new list from any iterable. .sort() changes a list in place and returns None.
reversed vs [::-1]reversed is a lazy iterator. The slice builds a full copy.
type vs isinstancetype(x) is C rejects subclasses. isinstance accepts them. Use isinstance.
== vs is== compares values. is compares identity, the same thing id() reports.
int() vs round()int truncates toward zero. round goes to the nearest, ties to even.
iter vs nextiter makes the walker. next takes one step with it.
list vs tupleLists change and are unhashable. Tuples are fixed and can be dict keys.
set() vs {}{} is an empty dict. The empty set only has one spelling: set().
vars vs dirvars gives the instance data. dir gives every name, methods included.

Looks built-in, is not

These feel like built-ins but need an import, or left the language entirely.

NameWhere it lives now
reducefunctools.reduce. Moved out in Python 3 on purpose.
sqrt, floor, ceil, gcd, logmath. Only abs, pow, round, divmod are built in.
accumulate, chain, product, combinationsitertools.
Counter, defaultdict, deque, namedtuplecollections.
partial, lru_cache, cachefunctools.
reloadimportlib.reload.
raw_input, xrange, unicode, long, cmp, execfile, applyGone in Python 3. Use input, range, str, int.
Built-in names that are not functions. The builtins module also holds True, False, None, NotImplemented, Ellipsis (...), __debug__, and every exception class from Exception down. Run len(dir(__builtins__)) to see the whole set for your version.

Recap

← Guide 6: ML and Data Guide 1: The Python Toolkit →