15 questions found
How do you use functools.partial to create a new function with some arguments pre-filled?
Advanced
functools.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
How does Python evaluate default argument expressions ONLY ONCE at definition time, and what SAFE pattern exploits this for genuinely useful caching?
Advanced
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
How do you correctly type-hint a function using *args and **kwargs with modern typing, like ParamSpec for preserving a wrapped function's exact signature?
Advanced
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
How would you implement a function that dynamically inspects its own call arguments at runtime using the 'inspect' module, useful for building generic validation or logging decorators?
Advanced
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
What does it mean that functions are 'first-class objects' in Python?
Beginner
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