Build a Progress Callback Function for Loops in Python
Create a reusable progress callback that receives per-step data and lets callers log or update a UI as a loop runs.
Python code
34 linesdef run_with_progress(items, desc="Processing", step_callback=None):
"""Run a loop with progress updates via callback."""
total = len(items)
for idx, item in enumerate(items):
# Process the item (simulated work here)
result = item * 2
# Build progress data dictionary
if step_callback:
progress_data = {
"index": idx + 1,
"total": total,
"percent": round((idx + 1) / total * 100, 1),
"item": item,
"result": result,
}
step_callback(progress_data)
return total
def print_progress(data):
"""Example step callback that logs progress to console."""
suffix = f"({data['index']}/{data['total']})"
print(
f"{data['percent']}% {suffix}: "
f"item={data['item']} -> result={data['result']}"
)
if __name__ == "__main__":
items = [10, 20, 30, 40, 50]
completed = run_with_progress(items, step_callback=print_progress)
print(f"\nCompleted {completed} items.")
Output
20.0% (1/5): item=10 -> result=20
40.0% (2/5): item=20 -> result=40
60.0% (3/5): item=30 -> result=60
80.0% (4/5): item=40 -> result=80
100.0% (5/5): item=50 -> result=100
Completed 5 items.
How it works
The run_with_progress function iterates over items and calls step_callback after each iteration, passing a dictionary with progress details like index, total, and percent. Using a callback decouples the loop logic from how progress is consumed—callers can log to console, update a progress bar, or send events to a UI without modifying the core function. The percent is computed as (idx + 1) / total * 100 and rounded to one decimal place. The function returns the total count so callers know how many items were processed even if the callback is omitted.
Common mistakes
- Forgetting to check if `step_callback` is provided before calling it, causing TypeError.
- Using zero-based `idx` as the displayed index, confusing users who expect 1-based numbering.
- Dividing by total if `items` is empty, leading to ZeroDivisionError.
Variations
- Use `tqdm` library for automatic progress bars instead of a custom callback.
- Implement the same pattern with a generator that yields progress data for the caller to consume.
Real-world use cases
- Reporting progress in a data processing ETL job while fetching rows from a database.
- Updating a web UI progress bar via WebSocket when running long-running background tasks.
- Logging processing milestones in a machine learning training loop to track epoch progress.
Sponsored
More from Functions & basics
- Add Type Hints to Function Parameters and Return in Python easy
- Benchmark list append vs comprehension in Python easy
- Build a Context Manager in Python with contextlib.contextmanager easy
- Cache expensive function with lru_cache in Python easy
- Calculate Time Difference Across Time Zones in Python easy
- Call a Function Dynamically by Name in Python easy
Keep learning
Related tutorials and quizzes for this topic.