15 questions foundfunctools.partial(func, *args, **kwargs) returns a NEW callable that, when invoked, calls the ORIGINAL function with the pre-filled arguments PLUS whatever additional arguments are passed at call time — a clean, explicit alternative to writing a small wrapper lambda for the same purpose.
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2) # 'exponent' pre-filled
print(square(5)) # 25, equivalent to power(5, exponent=2)
Real-world example Creating a specialized, pre-configured version of a generic function, like a square() function derived from a general power() function.
Common follow-ups: How does functools.partial differ from simply writing an equivalent lambda that calls the original function?
functools & Functional Programming Tools
Since a default value's expression is evaluated exactly once when the 'def' statement runs (not per-call), you can deliberately exploit this to CACHE an expensive computation as a function's own default argument — an unusual but valid technique for memoizing a value computed once and reused across all calls.
def expensive_computation():
print("Computing...")
return 42
def use_cached_value(value=expensive_computation()): # runs ONCE, at def time
return value
print(use_cached_value()) # 'Computing...' printed once, then 42
print(use_cached_value()) # just 42, NOT recomputed
Real-world example Deliberately caching an expensive, one-time initialization value as a function's default argument (an unusual but occasionally useful trick).
Common follow-ups: Why is this technique considered clever but potentially CONFUSING to other developers reading the code later?
functools & Functional Programming Tools
typing.ParamSpec (Python 3.10+) lets you capture an ENTIRE parameter signature (both positional and keyword parts) as a single type variable, letting a generic decorator's type hints correctly preserve the WRAPPED function's exact original signature — something plain *args: Any, **kwargs: Any annotations can't express.
from typing import ParamSpec, TypeVar, Callable
P = ParamSpec("P")
R = TypeVar("R")
def logged(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
Real-world example Writing a fully type-safe generic decorator whose type checker output correctly reflects the wrapped function's exact original parameter types.
Common follow-ups: How does a type checker like mypy actually USE ParamSpec to verify calls to the wrapped, decorated function?
Type Hints
inspect.signature(func).bind(*args, **kwargs) maps the ACTUAL passed arguments to their corresponding PARAMETER NAMES (correctly handling positional, keyword, defaults, *args, and **kwargs), letting you build generic tooling (like validation or logging) that needs to reason about arguments BY NAME regardless of how the caller actually passed them.
import inspect
def log_arguments(func):
sig = inspect.signature(func)
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
bound.apply_defaults()
print(f"Called with: {dict(bound.arguments)}")
return func(*args, **kwargs)
return wrapper
@log_arguments
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
greet("Sam") # logs: {'name': 'Sam', 'greeting': 'Hello'}
Real-world example Building a generic argument-logging or validation decorator that correctly maps ANY call style (positional or keyword) back to parameter names.
Common follow-ups: What does bound.apply_defaults() specifically add that raw sig.bind() alone wouldn't include?
Decorators
Functions can be assigned to variables, passed as arguments to other functions, returned from functions, and stored in data structures like lists or dicts -- treated just like any other value (an int or a string) rather than being a special, restricted kind of construct.
def greet():
return "Hello!"
say_hello = greet # assigned to a variable
functions = [greet, print] # stored in a list
def call_it(func): # passed as an argument
return func()
print(call_it(greet)) # "Hello!"
Real-world example Passing a specific comparison or transformation function as an argument to a generic sorting or processing utility.
Common follow-ups: How does this first-class function support directly enable Python's decorator syntax?
Decorators