Python Functions Quiz: Arguments, Scope, Recursion and Lambdas

Test your Python functions with 12 questions and three programs on return and None, default and keyword arguments, scope, closures, recursion and lambdas.

  • Course: Python study plan
  • Module: Functions
  • Kind: Checkpoint — cleared at 70%
  • Reading time: 25 min
  • Runtime: CPython 3.11

Checkpoint — Functions is the checkpoint that closes the Functions module: a graded quiz and whole-program exercises, passed at 70%.

Instructions

This checkpoint covers the whole module: def and return and the None that comes back when nothing is returned, the signature — positional, keyword, defaults, *args, **kwargs, * and / — and the mutable-default trap, LEGB scope with global, nonlocal, closures and late binding, recursion with its depth limit and memoisation, lambdas and the key-function idiom with map, filter, partial, reduce and operator, and type hints and docstrings.

How it works. Twelve questions and three programs. You need 70% on the questions and every program accepted to clear the module. You can retake it as often as you like; your best score counts.

Before you start, make sure you can answer these from memory:

  • What does a function return when it has no return, and which list methods return that on purpose?
  • Why is def f(xs=[]) a bug, and what is the idiom?
  • What is the difference between f(xs) and f(*xs)?
  • Why does count += 1 inside a function raise UnboundLocalError, and what are the two ways out?
  • What do three lambdas created in a for loop over i see when called later, and how do you fix it?
  • What does @functools.cache require of the function it wraps?
  • In sorted(pairs, key=lambda p: (-p[1], p[0])), what is the order?

The three programs are a memoised path counter over a grid with obstacles, a small argument-parsing function that accepts positional and keyword-style arguments and reports what it received, and a pipeline that composes a list of functions built by factories.

Common questions

What does a Python function return when it has no return statement?

It returns None. List methods that change the list in place, such as sort, append and extend, return None on purpose, so xs = xs.sort() throws the list away; sorted(xs) returns a new list instead.

What is the difference between f(xs) and f(*xs) in Python?

f(xs) passes the whole list as one argument; f(*xs) unpacks it and passes each element as a separate positional argument, so move(*point) with point = (3, 4) calls move(3, 4). Use the star when the function takes separate values rather than one sequence.

What does @functools.cache require of the function it wraps?

Hashable arguments, because they become dictionary keys — ints, strings and tuples, not lists — and a pure function: the same arguments always give the same result, with no side effects that a cached call would skip.

In sorted(pairs, key=lambda p: (-p[1], p[0])), what is the order?

By each pair's second value in descending order, since negating it reverses that criterion, and among equal second values by the first value in ascending order. Tuples compare element by element, which is what makes sorting by several keys work.

Exercises

Grid paths around obstacles

Count the paths from the top-left cell to the bottom-right cell of a grid, moving only right or down and never entering a # cell. Write a recursive paths(r, c) memoised with functools.cache: the answer for a cell is the sum of the answers for the cell to its right and the cell below it, with the bottom-right cell counting 1 and any blocked or out-of-range cell counting 0.

Input: R C, then R lines of . and #. Output: the number of paths.

3 3
...
.#.
...

prints

2

A keyword-only signature under test

Define connect(host, port=80, *, secure=False) returning host=<host> port=<port> secure=<secure>. Then read n call expressions written as Python source, evaluate each with eval in a try, and print the result — or TypeError when the call does not fit the signature (too many positional arguments, a missing host, an unknown keyword).

Input: n, then n call expressions. Output: one line per call.

3
connect("db")
connect("db", 443, secure=True)
connect("db", 1, True)

prints

host=db port=80 secure=False
host=db port=443 secure=True
TypeError

Compose

Build a pipeline of closures. The first line lists steps: add k, mul k and clamp lo hi; write a factory for each (make_add(k), make_mul(k), make_clamp(lo, hi)) that returns a one-argument function. Compose all the steps into a single function with functools.reduce and a compose(f, g) helper, then apply it to every integer on the second line.

Input: a line of steps (possibly empty), then a line of integers. Output: the results, space-separated.

add 1 mul 3 clamp 0 10
-5 0 2 5

prints

0 3 9 10

In this module: Functions

← Type hints and docstrings — the contract a function publishes · String basics — an immutable sequence of characters →