More Unpacking in Loops in Python for Data Science

Datatypes Mar 19, 2026

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 values
  • for key, (val1, val2) in data.items() → nested unpacking
  • for first, *rest in records → head/tail splitting
  • for **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 for statement for maximum readability
  • Use enumerate(zip(...)) whenever you need both rank and paired values
  • Use *rest to cleanly separate primary columns from the rest
  • Keep unpacking simple — avoid more than two levels of nesting
  • Combine with zip() and dict.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 a SyntaxError
  • Expecting *rest to 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 *rest for 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

Related Tutorials

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices