Functions and Comprehensions
Defining and calling functions plus what is built on them: parameter kinds, closures, lambdas, functools, comprehensions. One signature shows whether you know Python's evaluation model.
part ofPythonoverview, primer and where to startread it →on this pageshowhide
explore
- Parameters and Arguments21 questions
- *args and **kwargs3 questions
- Positional-Only and Keyword-Only4 questions
- Mutable Default Arguments3 questions
- Pass-by-Object Semantics3 questions
- PEP 448 Call-Site Unpacking4 questions
- Signature Introspection4 questions
- Closures and Scoping19 questions
- LEGB Scope Resolution4 questions
- global and nonlocal4 questions
- Late-Binding Closures in Loops3 questions
- UnboundLocalError and Local Detection4 questions
- Free Variables and Cells4 questions
- Lambda and Higher-Order Functions19 questions
- Lambda Expressions4 questions
- map, filter, and reduce4 questions
- Key Functions for sorted, min, and max3 questions
- The operator Module4 questions
- Function Objects and Attributes4 questions
- The functools Toolkit17 questions
- functools.partial4 questions
- lru_cache and cache Memoization5 questions
- singledispatch and Generic Functions4 questions
- total_ordering and cmp_to_key4 questions
- Comprehensions and Generator Expressions16 questions
- List, Dict, and Set Comprehensions4 questions
- Generator Expression Laziness4 questions
- Comprehension Scope and Walrus4 questions
- Nested Loops and Flattening4 questions
- Recursive Calls7 questions
- Depth Limits and RecursionError4 questions
- No Tail-Call Optimization3 questions
questions
99 · 6 sectionsWhat do *args and **kwargs collect in a Python function definition?
basics
~10 s*args collects the leftover positional arguments into a tuple; **kwargs collects the leftover keyword arguments into a dict. Both types are fixed, and both are empty rather than None when nothing extra is passed.
How do you write a Python function with an optional list parameter that starts empty each call?
basics
~20 sGive the parameter a default of None and build the list inside the body when the caller omitted it. Writing items=[] creates one list while the def statement runs, and every call that omits the argument shares that same list.
In Python, why does appending to a list parameter change the caller's list while rebinding that parameter does not?
basics
~20 sA call binds the parameter name to the same object the caller passed; nothing is copied. Assigning to that name repoints only the function's local name, while calling a method like append changes the one shared object.
What does the Python call f(*a, *b, **d1, **d2) pass to f?
basics
~20 sBoth iterables are flattened into one positional argument list, in source order, and both mappings are merged into the keyword arguments. Python 3.5 (PEP 448) allowed more than one star and double-star unpacking per call.
What does a bare `*` in a Python function signature such as `def f(a, *, b)` do?
basics
~10 sIt closes the positional part of the signature. Every parameter written after the bare * is keyword-only, so the caller must pass it by name: f(1, b=2) works and f(1, 2) raises TypeError.
What is a free variable in a Python nested function, and how does a cell keep it alive?
basics
~20 sA free variable is a name an inner function reads but does not bind, whose binding belongs to an enclosing function. CPython moves that variable out of the frame into a one-slot cell object, and the inner function holds the same cell, so the value outlives the enclosing call.
Why does UnboundLocalError appear when a function does `x = x + 1` and x is a module-level global?
basics
~20 sPython decides scope at compile time: an assignment to x anywhere in the body makes x local for the whole function, so the read on the right-hand side hits an unset local slot instead of the module-level global.
What is the difference between `global` and `nonlocal` in Python?
basics
~20 sglobal name makes assignments inside a function rebind the module-level name; nonlocal name rebinds the name in the nearest enclosing function. Without either declaration, an assignment simply creates a new local that shadows the outer one.
Why does every lambda in [lambda: i for i in range(3)] return 2?
basics
~20 sEach lambda captures the variable i itself, not a snapshot of its value at creation time. The lambdas are called after the comprehension has finished, when i holds its final value 2, so all three return 2.
What is Python's LEGB rule, and in what order does it resolve a bare name?
basics
~20 sLEGB is the order Python searches namespaces for a bare name: Local, then any Enclosing function scopes, then the module's Global namespace, then the built-ins. The first namespace holding the name wins; if none does, Python raises NameError.
Why are Python functions called first-class objects, and what does `def` create?
basics
~20 sA def statement runs at runtime, builds a function object and binds it to a name, exactly like an assignment. That object can be stored in a dict, passed as an argument or returned again, which is what first-class means.
What does operator.itemgetter(2, 0) return when called on a sequence?
basics
~10 sIt returns the two-element tuple (row[2], row[0]), in the order the indices were given. With a single index, operator.itemgetter returns that one item itself, not a one-element tuple.
What does the key= argument of Python's sorted() do?
basics
~10 skey= takes a one-argument callable applied to every element; sorted, min and max then compare the values it returns instead of the elements themselves. The elements come back unchanged, only reordered.
What is a Python lambda expression, and how does it differ from a def function?
basics
~20 sA lambda expression builds an anonymous function whose body is a single expression, returned implicitly. def is a statement that names the function and can contain any block of code. Both create the same kind of function object.
What do Python's built-in map() and filter() return, and how do you get a list?
basics
~20 sBoth return a lazy, one-shot iterator object, not a list. Nothing is computed until you iterate. Wrap the result in list() or tuple(), or feed it straight to a for loop, sum(), any() or all().
What does functools.lru_cache do to a function, and how does functools.cache differ?
basics
~20 sfunctools.lru_cache stores each call's result in a dictionary keyed by the arguments, so a repeat call returns the stored value without running the body. functools.cache, added in 3.9, is lru_cache(maxsize=None): unbounded, and it never evicts.
Why does functools.lru_cache raise TypeError when the function is called with a list?
basics
~20 sThe wrapper builds a dictionary key out of the call's arguments, so every argument must be hashable. A list is not, so the lookup raises TypeError before the function body ever runs. Pass a tuple, or a frozenset for set-like input.
Why should the __lt__ you pair with functools.total_ordering return NotImplemented?
basics
~10 sReturning NotImplemented for an operand type you do not handle lets Python try the other operand's reflected method and then raise a clear TypeError. Returning False instead answers a question you cannot answer, silently.
Why would you use functools.partial instead of a lambda for a callable that must be pickled?
basics
~20 spickle stores a function by its importable qualified name, and a lambda has none, so pickling one fails. A functools.partial defines its own reduction — the wrapped callable plus the bound arguments — so it pickles whenever those pieces are themselves picklable.
How does functools.singledispatch pick an implementation when several registered classes match the argument?
basics
~10 sIt takes the class of the first positional argument, walks that class's method resolution order, and uses the implementation registered for the nearest ancestor, falling back to object. Registration order is irrelevant; specificity decides.
In the comprehension [x for row in rows for x in row], which for clause is the outer loop?
basics
~20 sThe leftmost one. Clauses read left to right in the same order you would write nested for statements, so for row in rows is the outer loop and for x in row the inner one. The result is one flat list.
Why does a Python list comprehension's loop variable not leak into the enclosing scope?
basics
~20 sSince Python 3, a comprehension evaluates its body in its own implicit function scope, so the name bound by its for clause is local to the comprehension and gone afterwards. Python 2 list comprehensions leaked it; generator expressions never did.
In Python, how does `(x*x for x in nums)` differ from `[x*x for x in nums]`?
basics
~10 sThe square-bracket form builds the whole list in memory immediately. The parenthesised form builds a generator object that computes each value only when asked, uses near-constant memory, and can be iterated only once.
What are the list, dict and set comprehension forms in Python, and how do they differ?
basics
~20 sSquare brackets build a list, braces with a colon per item build a dict, and braces without a colon build a set. All three share one shape: an output expression, a for clause, and an optional trailing if filter.
In a Python comprehension, when does `if` filter items and when does it choose a value?
basics
~20 sAn if written after the for clause is a filter, so failing items are skipped and the result gets shorter. An if/else written before the for clause is a conditional expression choosing each emitted value, so every item still appears.
What does Python's sys.getrecursionlimit() cap, and what happens when a call chain exceeds it?
basics
~20 ssys.getrecursionlimit() reports the ceiling on how many Python frames may be stacked at once, 1000 by default. When a call would go past it, CPython raises RecursionError instead of letting the thread run out of stack.
Why does CPython still push a frame when a Python function returns a call in tail position?
basics
~20 sCPython performs no tail-call optimization: returning a call compiles to an ordinary call, so a new frame is pushed while the caller's frame stays alive to receive the result. Depth grows with every recursive step.
How do you rewrite a recursive tree walk as a Python `while` loop over an explicit stack?
basics
~10 sSeed a list with the root, then loop while it is non-empty: pop an item, do that node's work, and push its children. The list holds the pending work the frames used to hold.
Why is sys.setrecursionlimit(1_000_000) not a safe way to allow much deeper recursion?
basics
~10 ssys.setrecursionlimit() moves CPython's own frame counter; it does not enlarge the thread's machine stack. Push far past what that stack really holds and the interpreter can die outright instead of raising a catchable RecursionError.
A proxy whose __getattr__ delegates to self._client raises RecursionError when deep-copied — why?
basics
~20 sCopying builds an instance without running init, so _client is absent. Probing that blank object for a hook name falls through to getattr, which reads self._client, which is also absent, so getattr calls itself forever.