More Unpacking in Loops in Python for Data Science – Best Practices 2026
Advanced unpacking inside loops is a powerful Pythonic skill that makes data science code dramatically cleaner and more readable. Once you master enumerate(), zip(), *rest, and nested unpacking, your feature engineering, result processing, and configuration handling become much more elegant.
TL;DR — Advanced Unpacking Patterns
for idx, (a, b) in enumerate(zip(...))→ index + paired valuesfor key, (val1, val2) in data.items()→ nested unpackingfor first, *rest in records→ head/tail splittingfor **kwargs in config_list→ dictionary unpacking in loops
1. Enumerate + zip() – The Most Common Power Combo
features = ["amount", "quantity", "profit", "region"]
importance = [0.42, 0.31, 0.18, 0.09]
for rank, (feature, score) in enumerate(zip(features, importance), start=1):
print(f"#{rank:2d} {feature:12} importance: {score:.4f}")
2. Nested Unpacking in Loops
results = [
("random_forest", (200, 10, 42)), # model, (n_estimators, max_depth, random_state)
("xgboost", (300, 8, 123))
]
for model_name, (n_est, depth, seed) in results:
print(f"{model_name:15} → n_estimators={n_est}, max_depth={depth}, seed={seed}")
3. Head/Tail Unpacking with *rest
feature_list = ["customer_id", "order_date", "amount", "quantity", "profit", "region", "category"]
primary, *metrics, geo = feature_list
print(f"Primary: {primary}")
print(f"Metrics: {metrics}")
print(f"Geo: {geo}")
4. Real-World Data Science Examples
import pandas as pd
# Small inline sample so this example runs as-is.
# In a real project you would load it: df = pd.read_csv("sales_data.csv")
df = pd.DataFrame({
"customer_id": [101, 102, 103, 104, 105, 106],
"customer_name": ["Ana", "Ben", "Chen", "Dana", "Eli", "Fay"],
"order_date": ["2026-01-05", "2026-01-12", "2026-02-03",
"2026-02-18", "2026-03-01", "2026-03-09"],
"amount": [1250.75, 890.50, 2340.00, 1780.25, 2615.40, 450.00],
"quantity": [3, 2, 5, 4, 6, 1],
"profit": [312.69, 222.63, 585.00, 445.06, 653.85, 112.50],
"region": ["North", "South", "East", "North", "West", "South"],
"category": ["Electronics", "Books", "Electronics", "Home", "Home", "Books"],
"rating": [5, 4, None, 3, 5, 4],
})
# Example 1: Unpacking column groups in a loop
numeric_cols = [col for col in df.columns if pd.api.types.is_numeric_dtype(df[col])]
# pandas 3 uses the "str" dtype for text, so test for strings rather than "object"
cat_cols = [col for col in df.columns if pd.api.types.is_string_dtype(df[col])]
for group_name, cols in [("Numeric", numeric_cols), ("Categorical", cat_cols)]:
print(f"{group_name} features ({len(cols)}): {cols[:5]}...")
# Example 2: Processing model hyperparameter grids
hyperparams = [
("rf", {"n_estimators": 200, "max_depth": 10}),
("xgb", {"n_estimators": 300, "learning_rate": 0.1})
]
for model_type, params in hyperparams:
print(f"Training {model_type} with {params}")
5. Best Practices in 2026
- Always unpack directly in the
forstatement for maximum readability - Use
enumerate(zip(...))whenever you need both rank and paired values - Use
*restto cleanly separate primary columns from the rest - Keep unpacking simple — avoid more than two levels of nesting
- Combine with
zip()anddict.items()for the cleanest code
The Rules of Unpacking
Unpacking assigns the items of any iterable to several names in one statement. The number of names must match the number of items, unless one name is starred: first, *middle, last = values puts everything that is left over into a list called middle. Only one starred name is allowed per assignment, and the starred list may be empty.
The same rules apply in the target of a for loop, and the shape of the names must mirror the shape of the data. That is why for i, (name, score) in enumerate(pairs) needs the parentheses: enumerate yields (index, pair), and the pair is itself unpacked.
Worked Example: Parsing Log Rows
rows = [
("2026-03-01", "INFO", "load", "ok"),
("2026-03-01", "ERROR", "train", "out of memory", "retry 1"),
("2026-03-02", "WARN", "eval"),
]
for line_no, (date, level, step, *details) in enumerate(rows, start=1):
message = " | ".join(details) if details else "-"
print(f"{line_no}: [{level:5}] {date} {step:6} {message}")
# Swap values without a temporary variable
low, high = 10, 3
if low > high:
low, high = high, low
print(low, high) # 3 10
# Ignore values you do not need with _
_, level, *_ = rows[1]
print(level) # ERROR
Rows have different lengths, yet the starred *details handles all of them without index arithmetic or length checks.
Common Mistakes
- Forgetting the parentheses in
for i, (a, b) in enumerate(pairs)— without them Python expects three values - Using two starred names (
*a, *b = data) — this is aSyntaxError - Expecting
*restto be a tuple — it is always a list - Unpacking a dict directly —
a, b = {"x": 1, "y": 2}gives the keys; use.items()for pairs
Quick Summary
- Mirror the data's shape in your loop targets, using parentheses for nested tuples
- Use
*restfor variable-length rows and_for values you ignore - Combine
enumerate(),zip()and unpacking for index-free loops
Conclusion
Advanced unpacking in loops is one of the hallmarks of professional Python data science code in 2026. It eliminates manual indexing, reduces boilerplate, and makes your feature processing, model configuration, and result handling far more elegant. Master enumerate(zip()), nested unpacking, and *rest, and your code will feel significantly more Pythonic and maintainable.
Next steps:
- Review your current loops and refactor any that use manual counters or index access into clean unpacking patterns