16.Functions II — Scope, Closures, *args/**kwargs
In this section, we dive deeper into Python functions by exploring scope rules, closures, and variadic arguments (*args and **kwargs). Understanding these concepts is essential for writing clean, maintainable, and powerful Python code.
1. Scope Rules (LEGB)
Python uses the LEGB rule to resolve variable names:
- L – Local: Names defined inside the current function.
- E – Enclosing: Names in enclosing functions (nested functions).
- G – Global: Names defined at the top-level of a module.
- B – Built-in: Names preassigned in Python (e.g., len, range).
Example:
x = ‘global’
def outer():
x = ‘enclosing’
def inner():
x = ‘local’
print(x)
inner()
outer() # Output: local
Best Practice: Avoid excessive reliance on global variables; prefer passing arguments explicitly.
2. Closures
A closure occurs when a nested function captures variables from its enclosing scope even after the outer function has finished executing.
Example:
def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply
times3 = make_multiplier(3)
print(times3(10)) # Output: 30
Closures are useful for creating function factories and maintaining state without using global variables.
3. Variadic Arguments: *args and **kwargs
*args allows a function to accept any number of positional arguments as a tuple. **kwargs allows any number of keyword arguments as a dictionary.
Example:
def demo(*args, **kwargs):
print(“Positional:”, args)
print(“Keyword:”, kwargs)
demo(1, 2, 3, name=’Alice’, age=30)
Best Practice: Use *args and **kwargs for flexible APIs, but document expected arguments clearly.
Advanced Features and Best Practices
Use closures for decorators and callbacks.
- Understand LEGB to avoid NameError and unintended shadowing.
- Avoid modifying global state inside functions.
- Combine *args and **kwargs with keyword-only arguments for clarity.