This rule raises an issue when several elements are added to the same list through consecutive append() calls, either as a sequence of statements or through a for-loop.

Why is this an issue?

Adding elements to a list one at a time is unnecessary when the extend() method exists. This happens with a sequence of append() statements:

my_list.append(first)
my_list.append(second)
my_list.append(third)

It also happens when a for-loop appends every element of another collection, which is the same sequence of calls spread over an iteration:

for element in other_collection:
    my_list.append(element)

The extend() method combines all additions into a single call and avoids the overhead of repeated append() invocations:

my_list.extend([first, second, third])
my_list.extend(other_collection)

What is the potential impact?

Grouping the additions makes the intent explicit: the reader sees one operation that grows the list instead of a series of independent statements. It also removes redundant call overhead, since the attribute lookup and the method call are resolved once instead of once per element. In hot code or when the source collection is large, the difference becomes measurable.

Exceptions

No issue is raised when other statements are interleaved between the append() calls, or when the body of the loop does more than append the loop variable, for example when it filters or transforms the elements:

for element in other_collection:
    if element.is_valid():
        my_list.append(element)

How to fix it

Group the appended elements into a single extend() call. For consecutive statements, pass the elements as a list literal. For a loop, pass the iterated collection directly, as extend() accepts any iterable.

Code examples

Noncompliant code example

def build_command(base_command, path):
    command = list(base_command)
    command.append("--archive")  # Noncompliant
    command.append("--verbose")
    command.append(path)
    return command

Compliant solution

def build_command(base_command, path):
    command = list(base_command)
    command.extend(["--archive", "--verbose", path])
    return command

Noncompliant code example

def collect_results(results, new_results):
    for result in new_results:
        results.append(result)  # Noncompliant
    return results

Compliant solution

def collect_results(results, new_results):
    results.extend(new_results)
    return results

How does this work?

extend() iterates over its argument and appends every element to the list, so the result is exactly the same as the individual calls. The augmented assignment my_list += [first, second, third] is equivalent to extend() and can be used instead when it reads better.

Pitfalls

Do not confuse extend() with append(): my_list.append(other_collection) adds the collection itself as a single element, while extend() adds its elements.

A loop that appends to the list it iterates over never terminates, while my_list.extend(my_list) simply doubles the list. The fix is behavior-preserving in every other case, so make sure the loop you replace iterates over a different collection.

Resources

Documentation

Related rules