tqdm
This guide defines best practices for using the `tqdm` library to implement clear, efficient, and robust progress bars in Python applications, ensuring consistent user feedback and maintainable code.
The skill
# tqdm Best Practices
`tqdm` is the definitive library for adding progress bars to Python applications. It's lightweight, fast, and works across various environments (terminal, Jupyter). This guide outlines the mandatory practices for its use.
## 1. Import Strategy
Always import `tqdm` directly from the library for clarity. Use `trange` as a convenient shortcut for `tqdm(range(N))`.
❌ BAD: Generic import
```python
import tqdm
for i in tqdm.tqdm(range(100)):
pass
```
✅ GOOD: Direct and clear imports
```python
from tqdm import tqdm, trange
for i in trange(100): # Use trange for simple range loops
pass
data = [1, 2, 3] * 10
for item in tqdm(data, desc="Processing items"): # Use tqdm for iterables
pass
```
## 2. Prefer Context Managers
Use `tqdm` as a context manager (`with ... as pbar:`) whenever possible. This ensures the progress bar is properly closed and resources are released, even if exceptions occur.
❌ BAD: Manual `close()` (prone to errors)
```python
pbar = tqdm(total=100)
for i in range(100):
pbar.update(1)
# What if an error occurs here? pbar.close() might be skipped.
pbar.close()
```
✅ GOOD: Context manager for robust cleanup
```python
with tqdm(total=100, desc="Calculating") as pbar:
for i in range(100):
# Simulate work
if i == 50:
# Example of an error, pbar will still close
# raise ValueError("Something went wrong!")
pass
pbar.update(1)
```
## 3. Manual Updates for Non-Iterables or Custom Logic
When wrapping an iterable isn't feasible (e.g., callbacks, async operations, or custom progress logic), use `pbar.update(n)`. **Always provide `total`** when using manual updates.
❌ BAD: Missing `total` with manual updates (bar won't show percentage/ETA)
```python
pbar = tqdm(desc="Downloading")
# ... later in a callback ...
pbar.update(chunk_size)
```
✅ GOOD: Explicit `total` for manual updates
```python
import time
import random
file_size = 1024 * 1024 * 50 # 50 MB
downloaded_bytes = 0
with tqdm(total=file_size, unit="B", unit_scale=True, desc="Downloading file") as pbar:
while downloaded_bytes < file_size:
chunk = random.randint(1024, 1024 * 10) # Simulate chunk download
chunk = min(chunk, file_size - downloaded_bytes)
downloaded_bytes += chunk
pbar.update(chunk)
time.sleep(0.01)
```
## 4. Use Descriptive Labels and Postfix Information
Enhance readability by providing a `desc` (description) and dynamic `postfix` information. `set_description()` and `set_postfix()` are preferred for updating these dynamically.
❌ BAD: Static or cluttered description
```python
for epoch in tqdm(range(10), desc="Epoch"):
loss = 0.5 - epoch * 0.01
# This doesn't update dynamically without re-creating the bar
for batch in tqdm(range(100), desc=f"Epoch {epoch} Loss: {loss:.4f}", leave=False):
pass
```
✅ GOOD: Dynamic description and postfix updates
```python
import random
for epoch in tqdm(range(10), desc="Training Epochs"):
total_loss = 0.0
for batch in trange(100, desc=f"Batch {epoch+1}", leave=False):
current_loss = random.uniform(0.1, 0.5)
total_loss += current_loss
# Update postfix with real-time metrics
tqdm.set_postfix(loss=f"{current_loss:.4f}", avg_loss=f"{total_loss/(batch+1):.4f}")
# Simulate work
time.sleep(0.005)
```
## 5. Handle Output with `tqdm.write()`
Never use `print()` directly when a `tqdm` bar is active. It will corrupt the progress bar display. Use `tqdm.write()` instead, which intelligently prints messages without interfering.
❌ BAD: Corrupts progress bar
```python
for i in trange(10):
if i == 5:
print("Mid-loop message!") # This will mess up the bar
time.sleep(0.1)
```
✅ GOOD: Preserves progress bar integrity
```python
for i in trange(10, desc="Processing"):
if i == 5:
tqdm.write("Mid-loop message: Halfway there!")
time.sleep(0.1)
```
## 6. Fine-tune Display Options
Use `leave=False` for inner loops to remove them upon completion, keeping the terminal clean. Leverage `unit`, `unit_scale`, and `dynamic_ncols` for better presentation.
❌ BAD: Cluttered terminal with nested bars
```python
for i in tqdm(range(3), desc="Outer"):
for j in tqdm(range(5), desc="Inner"): # leave=True by default
time.sleep(0.05)
```
✅ GOOD: Clean nested bars and scaled units
```python
for i in tqdm(range(3), desc="Outer Loop"):
for j in tqdm(range(5), desc="Inner Loop", leave=False): # Bar disappears after inner loop finishes
time.sleep(0.05)
# Example with units
with tqdm(total=100_000_000, unit="B", unit_scale=True, dynamic_ncols=True, desc="Transferring") as pbar:
for _ in range(100):
pbar.update(1_000_000)
time.sleep(0.01)
```
## 7. Integrate with Data Science Libraries
`tqdm.pandas()` and `tqdm.auto` provide seamless integration for common data science workflows.
### Pandas DataFrames
Use `tqdm.pandas()` to add a `.progress_apply()` method to DataFrames.
```python
import pandas as pd
from tqdm.auto import tqdm # Use tqdm.auto for notebook compatibility
tqdm.pandas() # Register tqdm with pandas
df = pd.DataFrame({'col': range(100000)})
# Applying a function with a progress bar
df['new_col'] = df['col'].progress_apply(lambda x: x * 2)
```
### Jupyter Notebooks
`tqdm.auto` automatically detects if it's running in a Jupyter environment and uses the appropriate display.
```python
# In a .py file that might be run in a notebook or terminal
from tqdm.auto import tqdm, trange
def long_running_task():
for i in trange(100, desc="Auto-detected progress"):
time.sleep(0.01)
long_running_task()
```
## 8. Performance Considerations
`tqdm` is highly optimized, but for extremely fast loops (e.g., millions of iterations per second), consider batching updates or disabling the bar if not strictly necessary.
```python
# For loops where each iteration is extremely fast
total_iterations = 10_000_000
batch_size = 1000
with tqdm(total=total_iterations, desc="Batch Processing") as pbar:
for i in range(0, total_iterations, batch_size):
# Perform batch_size operations here
# ...
pbar.update(batch_size) # Update once per batch
```
How to install it
1. Open the tool and start a new conversation, project or template. 2. Paste the text below wherever the tool keeps standing instructions — usually called system prompt, instructions, or persona. 3. Save it so it applies to every run rather than a single message.