Python Functions Cheat Sheet
1 · The lesson
readA function is a named block of code that you can run again and again. You give it values, it does its work, and it can send a result back with return. This page is a short reference with examples you can run. For the full tutorials, see Functions, Lambda and Decorators.
1. def and return
def names the function. return sends a value back to the code that called the function. A function with no return line sends back None.
def total(price, qty): return price * qty print(total(2.5, 4)) # 10.0 def greet(name): print("Hello", name) # prints, but returns nothing result = greet("Ada") # Hello Ada print(result) # None
2. Parameters and arguments
A parameter is a name in the def line (price, rate). An argument is the value you pass in the call (10, 0.1). You can pass an argument by position (in the same order as the parameters) or by keyword (name=value, in any order). A parameter with = has a default value: if you leave that argument out, Python uses the default.
def with_tax(price, rate=0.2): return round(price * (1 + rate), 2) print(with_tax(10)) # 12.0 rate uses its default print(with_tax(10, 0.1)) # 11.0 passed by position print(with_tax(rate=0.5, price=10)) # 15.0 passed by keyword, in any order
with_tax() raises TypeError: price has no default, so you must pass it.
3. *args and **kwargs
A * before a parameter collects all the extra arguments passed by position into a tuple. A ** collects all the extra name=value arguments into a dict. The names args and kwargs are only a habit: *nums works the same.
def show(*args, **kwargs): return args, kwargs print(show(1, 2)) # ((1, 2), {}) print(show(size="L", qty=3)) # ((), {'size': 'L', 'qty': 3}) def add_all(*nums): # takes any number of values return sum(nums) print(add_all(4, 5, 6)) # 15 print(add_all()) # 0
setup added so this can run · defines args, kwargs, nums
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) args = _AutoMock('args') kwargs = _AutoMock('kwargs') nums = _AutoMock('nums')
4. * and ** in a call: unpack
In a call, * does the opposite: it spreads a list into separate arguments. ** spreads a dict into name=value arguments.
order = [2.5, 4] print(total(*order)) # 10.0 same as total(2.5, 4) order = {"price": 3, "qty": 2} print(total(**order)) # 6 same as total(price=3, qty=2)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) def total(*_a, **_kw): print('-> total() called') return _AutoMock('total()') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'def total(price, qty):\n return price * qty\n\nprint(total(2.5, 4)) # 10.0\n\ndef greet(name):\n print("Hello", name) # prints, but returns nothing\n\nresult = greet("Ada") # Hello Ada\nprint(result) # None', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
5. / and * in the def line
Names before / must be passed by position. Names after * must be passed by keyword. You will see / and * in library functions more often than you will write them yourself.
def move(x, y, /, *, speed=1): return x, y, speed print(move(1, 2, speed=5)) # (1, 2, 5)
setup added so this can run · defines x, y
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) x = _AutoMock('x') y = _AutoMock('y')
move(1, 2, 5) raises TypeError because speed must be named. move(x=1, y=2) raises TypeError because x and y must not be named.
6. Return several values
return a, b sends back one tuple that holds two values. You can unpack the tuple into two names: each name gets one value.
def min_max(scores): return min(scores), max(scores) print(min_max([70, 92, 81])) # (70, 92) low, high = min_max([70, 92, 81]) print(high) # 92
7. Scope: where a name can be used
A name you set inside a function is local: it is a new name that exists only inside that function. It does not change a name with the same spelling outside.
score = 10 def reset(): score = 0 # a new local name reset() print(score) # 10
global lets a function change a name from outside. Use it rarely: returning the new value is clearer. nonlocal lets an inner function change a name that belongs to the function around it. Below, step() changes n, which belongs to counter(), so n is kept between calls.
def add_point(): global score score += 1 add_point() print(score) # 11 def counter(): n = 0 def step(): nonlocal n n += 1 return n return step step = counter() print(step(), step()) # 1 2
setup added so this can run · re-runs 1 earlier block on this page first
score = 1 # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'score = 10\n\ndef reset():\n score = 0 # a new local name\n\nreset()\nprint(score) # 10', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
8. lambda and functions as values
A function is a value, like a number or a list. You can pass it to another function, such as sorted() or map(). Pass the function itself, without (). lambda makes a small function with no name, written in one expression.
people = [("Ada", 36), ("Linus", 28), ("Grace", 45)] # lambda p: p[1] takes p and returns p[1], the age print(sorted(people, key=lambda p: p[1])) # [('Linus', 28), ('Ada', 36), ('Grace', 45)] print(list(map(str.upper, ["ada", "hedy"]))) # ['ADA', 'HEDY'] size = len # another name for the same function print(size("Grace")) # 5
9. Docstrings and type hints
The string on the first line of a function is its docstring: help() shows it. Type hints say which types you expect. Python does not check them when the code runs; tools such as mypy and your editor do.
def area(w: float, h: float) -> float: """Area of a w by h rectangle.""" return w * h print(area.__doc__) # Area of a w by h rectangle. print(area(2, 3)) # 6 print(area("ab", 3)) # ababab
10. A tiny decorator
A decorator takes a function and returns a new function. The new function calls the old one and does some extra work. @shout above a def is the same as writing hello = shout(hello) after it.
def shout(func): def wrapper(name): return func(name).upper() return wrapper @shout def hello(name): return f"hello {name}" print(hello("Hedy")) # HELLO HEDY
Common mistakes
A list as a default value. The default list is made once, when Python reads the def line, and every call shares it:
def add(item, cart=[]): cart.append(item) return cart print(add("tea")) # ['tea'] print(add("pie")) # ['tea', 'pie'] def add(item, cart=None): # the fix if cart is None: cart = [] cart.append(item) return cart print(add("tea")) # ['tea'] print(add("pie")) # ['pie']
Forgetting return. The function calculates the value and then loses it, so the call gives back None:
def double(n): n * 2 print(double(4)) # None
print instead of return. print only shows a value on the screen. If the caller needs the value, use return. With def total(a, b): print(a + b), the line total(1, 2) + 1 raises TypeError, because total gives back None.
Calling instead of passing. sorted(names, key=len()) raises TypeError. Pass the function, key=len. len is the function; len() calls it.
Using a built-in name. After list = ["tea"], the line list("ab") raises TypeError: 'list' object is not callable. Choose another name, such as items.
Found this useful? Share it with a friend who’s learning Python.