The run_n_times() decorator is a classic parameterized decorator example in Python — it takes an integer n and returns a decorator that wraps the target function to execute it n times on each call. This pattern is useful for repeated execution (e.g., stress testing, Monte Carlo simulations, retry logic with fixed count, or benchmarking average performance), and it demonstrates the decorator factory pattern: outer function accepts parameters, returns inner decorator, which returns wrapper. In 2026, @run_n_times(n) remains a go-to teaching example and real-world utility — often extended for logging each run, collecting results, handling exceptions, or integrating with pandas/Polars for repeated data transformations or simulations.
Here’s a complete, practical guide to the run_n_times decorator: basic implementation, collecting results, error handling, real-world usage patterns, and modern best practices with type hints, functools.wraps, performance considerations, and pandas/Polars integration.
Basic implementation — repeats the function n times, returns the last result (or None if no return needed).
from functools import wraps
def run_n_times(n: int):
"""Decorator factory: runs the function n times on each call."""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = None
for _ in range(n):
result = func(*args, **kwargs) # last result wins
return result
return wrapper
return decorator
Usage — decorate any function; it executes n times when called.
@run_n_times(n=3)
def say_hello(name: str) -> None:
print(f"Hello, {name}!")
say_hello("Alice")
# Hello, Alice!
# Hello, Alice!
# Hello, Alice!
Collecting results — modify to return list of all results instead of just the last one.
def run_n_times_collect(n: int):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
results = []
for _ in range(n):
results.append(func(*args, **kwargs))
return results
return wrapper
return decorator
@run_n_times_collect(n=4)
def random_number() -> int:
import random
return random.randint(1, 100)
print(random_number())
# [42, 17, 89, 31] (example random results)
Real-world pattern: repeated pandas/Polars simulations or benchmarks — run transformations multiple times and average results.
import pandas as pd
import numpy as np
@run_n_times_collect(n=5)
def simulate_noise(df: pd.DataFrame) -> pd.DataFrame:
"""Add random noise to 'value' column."""
noise = np.random.normal(0, 5, size=len(df))
return df.assign(value_noisy=df['value'] + noise)
df = pd.DataFrame({'value': [100, 200, 300]})
results = simulate_noise(df)
print(f"Average noisy value: {pd.concat(results)['value_noisy'].mean():.2f}")
Best practices make run_n_times decorators safe, readable, and performant. Always use @wraps(func) — preserves name, docstring, type hints. Use type hints — def run_n_times(n: int) -> Callable[[Callable], Callable] — improves clarity and mypy checks. Handle return values — decide whether to return last result, list of results, or average (customize per use case). Modern tip: use Polars lazy API — decorate .collect() calls for repeated expensive operations. Add logging — log each run or total time if needed. Use *args, **kwargs — makes decorator generic. Return meaningful value — e.g., list for collect mode. Handle exceptions — wrap in try/except if runs should be independent. Test decorator — assert correct number of executions and results. Combine with timeit — benchmark repeated calls. Use tenacity — for retry instead of fixed repeats. Avoid on generators — can break iteration; use wrappers carefully. Stack with other decorators — order matters (@run_n_times(3) @timer).
The run_n_times(n) decorator factory repeats the decorated function n times per call — ideal for simulations, stress testing, or repeated transformations. In 2026, use @wraps, type hints, result collection, Polars lazy profiling, and test thoroughly. Master this pattern, and you’ll write reusable decorators that add repeatable execution cleanly and efficiently.
Next time you need to run a function multiple times — use @run_n_times(n). It’s Python’s cleanest way to say: “Execute this n times — automatically.”