This rule raises an issue when several elements are added to the same set through consecutive add() calls, either as a sequence of
statements or through a for-loop.
Adding elements to a set one at a time is unnecessary when the update() method exists. This happens with a sequence of
add() statements:
my_set.add(first) my_set.add(second) my_set.add(third)
It also happens when a for-loop adds every element of another collection, which is the same sequence of calls spread over an iteration:
for element in other_collection:
my_set.add(element)
The update() method combines all additions into a single call and avoids the overhead of repeated add() invocations:
my_set.update({first, second, third})
my_set.update(other_collection)
Grouping the additions makes the intent explicit: the reader sees one operation that grows the set 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.
No issue is raised when other statements are interleaved between the add() calls, or when the body of the loop does more than add the
loop variable, for example when it filters or transforms the elements:
for element in other_collection:
if element.is_valid():
my_set.add(element)
Group the added elements into a single update() call. For consecutive statements, pass the elements as a set literal. For a loop, pass
the iterated collection directly, as update() accepts any iterable.
def collect_tags(base_tags, name):
tags = set(base_tags)
tags.add("archived") # Noncompliant
tags.add("verified")
tags.add(name)
return tags
def collect_tags(base_tags, name):
tags = set(base_tags)
tags.update({"archived", "verified", name})
return tags
def collect_results(results, new_results):
for result in new_results:
results.add(result) # Noncompliant
return results
def collect_results(results, new_results):
results.update(new_results)
return results
update() iterates over its argument and adds every element to the set, so the result is exactly the same as the individual calls. The
augmented assignment my_set |= {first, second, third} is equivalent to update() with another set and can be used instead
when it is more readable.
Do not confuse update() with add(): my_set.add(other_collection) adds the collection itself as a single
element, and fails when that collection is not hashable, while update() adds its elements.
A loop that adds to the set it iterates over only re-adds elements that are already present, so it leaves the set unchanged and there is nothing to group. Apart from that case, the fix preserves behavior.
set.update() methodset,
frozenset