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
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
Any assignment to a name in a function (including
+=) makes it local for the whole function, so reading it first raisesUnboundLocalError. Useglobalfor module names andnonlocalfor enclosing function names. - 3
Default arguments are evaluated once, when
defruns, and stored in__defaults__/__kwdefaults__. UseNoneas the sentinel for mutable defaults and test it withis None. - 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
Stacked decorators are applied bottom-up and their wrappers run top-down. Decorator factories (
@deco(arg)) need an extra level of nesting, andfunctools.wrapscopies metadata and sets__wrapped__. - 6
lru_cachekeys on hashable arguments, treats keyword and positional calls as different keys, and on methods shares one cache that keeps everyselfalive. - 7
Parameter order is positional-only, normal,
*argsor bare*, keyword-only,**kwargs. Positional-only names can be reused as**kwargskeys.
Common traps
bucket = bucket or []throws away a caller's empty list; checkif bucket is Noneinstead.except E as e:deleteseat the end of the block, even ifeexisted before.A
returninfinallysilently swallows an in-flight exception (a SyntaxWarning from Python 3.14).
Read the source
Test yourself on Functions, Scope, Closures and Decorators
Ten questions, with the answer and explanation after each one.