Avoid Unnecessary Materialization

ID

python.avoid_unnecessary_materialization

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

medium

Resource

Efficiency

Language

Python

Tags

best-practice, efficiency

Description

Reports list(…​) or tuple(…​) calls whose only consumer is a function that already accepts an iterable (sum, min, max, any, all, len, set, sorted, tuple, frozenset, next, iter). Wrapping a generator or iterable in a list just to pass it to such a function allocates an intermediate container for no benefit; passing the iterable directly avoids the copy.

sum(list(x * x for x in items))      # FLAW — sum already takes an iterable
any(list(p(x) for x in items))       # FLAW — any short-circuits, list defeats it

sum(x * x for x in items)            # OK
any(p(x) for x in items)             # OK

Rationale

The intermediate list(…​) materialises every element before the consumer runs, even when the consumer would short-circuit (any, all, next). For any over a million-element iterable that would have stopped at the first match, the wrap turns an O(1) operation into O(n).

Remediation

Drop the list(…​) wrap:

total = sum(x * x for x in items)
matches = any(predicate(x) for x in items)
ordered = sorted(generate_data())

Keep the wrap only when the consumer needs a true list (e.g. random.sample, pd.DataFrame, indexing).