Study notes · 10% of the exam

Functions, Scope, Closures and Decorators

Know how Python resolves names (LEGB), when a name becomes local, how closures capture variables rather than values, and how decorators and functools wrap functions.

Key points

  1. 1

    Name lookup is Local, Enclosing, Global, Built-in. Class bodies are not an enclosing scope for methods, lambdas, generator expressions or comprehension bodies inside them.

  2. 2

    Any assignment to a name in a function (including +=) makes it local for the whole function, so reading it first raises UnboundLocalError. Use global for module names and nonlocal for enclosing function names.

  3. 3

    Default arguments are evaluated once, when def runs, and stored in __defaults__ / __kwdefaults__. Use None as the sentinel for mutable defaults and test it with is None.

  4. 4

    Closures capture variables, not values, so lambdas created in a loop all see the final value. Freeze values with a default argument or functools.partial.

  5. 5

    Stacked decorators are applied bottom-up and their wrappers run top-down. Decorator factories (@deco(arg)) need an extra level of nesting, and functools.wraps copies metadata and sets __wrapped__.

  6. 6

    lru_cache keys on hashable arguments, treats keyword and positional calls as different keys, and on methods shares one cache that keeps every self alive.

  7. 7

    Parameter order is positional-only, normal, *args or bare *, keyword-only, **kwargs. Positional-only names can be reused as **kwargs keys.

Common traps

  • bucket = bucket or [] throws away a caller's empty list; check if bucket is None instead.

  • except E as e: deletes e at the end of the block, even if e existed before.

  • A return in finally silently swallows an in-flight exception (a SyntaxWarning from Python 3.14).

Test yourself on Functions, Scope, Closures and Decorators

Ten questions, with the answer and explanation after each one.