Promote predictable and secure for loops by iterating over a copy or new collection item as described in 4.2 for
Statements Python 3.9 2024.
In-place modification of mutable types such as list
, dict
, or set
that are part of a for loop can result in unpredictable outcomes.
This noncompliant01.py
example will successfully remove the Bob from userlist
but this modifies the original list userlist
and is not recommended.
""" Non-compliant Code Example """
userlist = ['Alice', 'Bob', 'Charlie']
print(f'Unmodified list: {userlist}')
for user in userlist:
if user == 'Bob':
userlist.remove(user)
print(f'Modified list: {userlist}')
This noncompliant02.py
example attempts to delete a dictionary entry, which will result in a RuntimeError: Dictionary changed size during iteration error
being thrown.
""" Non-compliant Code Example """
userdict = {'Alice': 'active', 'Bob': 'inactive', 'Charlie': 'active'}
print(f'Unmodified dict: {userdict}')
for user, status in userdict.items():
if status == 'inactive':
del userdict[user]
print(f'Modified dict: {userdict}')
The noncompliant03.py
example attempts to remove Bob from userset
, which will result in a RuntimeError: Set changed size during iteration error
being thrown.
""" Non-compliant Code Example """
userset = {'Alice', 'Bob', 'Charlie'}
print(f'Unmodified set: {userset}')
for user in userset:
if user == 'Bob':
userset.remove(user)
The compliant01.py
solution demonstrates both strategies. The first example creates a copy of the userlist
list to be iterated over, and then removes the item from the original userlist . The second examples creates a new list activeusers
and adds all users except Bob to activeusers
.
""" Compliant Code Example """
userlist = ['Alice', 'Bob', 'Charlie']
print(f'Unmodified list: {userlist}')
# Create a copy
for user in userlist.copy():
if user == 'Bob':
userlist.remove(user)
print(f'Modified list: {userlist}')
# Create a sample collection: list
userlist2 = ['Alice', 'Bob', 'Charlie']
print(f'Unmodified list: {userlist2}')
# Create new list
activeusers = []
for user in userlist2:
if user != 'Bob':
activeusers.append(user)
print(f'New list: {activeusers}')
The requirement to use copy()
or deepcopy()
will vary depending on the problem that needs to be solved.
The compliant02.py
code demonstrates both strategies. The first example creates a copy of the userdict dict to be iterated over, and then removes the item from the original userdict
. The second examples creates a new dict activeusersdict
, and adds the active users to activeusers
.
""" Compliant Code Example """
userdict = {'Alice': 'active', 'Bob': 'inactive', 'Charlie': 'active'}
print(f'Unmodified dict: {userdict}')
# Create a copy
for user, status in userdict.copy().items():
if status == 'inactive':
del userdict[user]
print(f'Modified dict: {userdict}')
# Create new dict
userdict2 = {'Alice': 'active', 'Bob': 'inactive', 'Charlie': 'active'}
activeuserdict = {}
for user, status in userdict2.items():
if status != 'inactive':
activeuserdict.update({user: status})
print(f'New dict: {activeuserdict}')
The requirement to use copy()
or deepcopy()
will vary depending on the problem that needs to be solved.
The compliant03.py
code demonstrates both strategies. The first example creates a copy of the userset
set to be iterated over, and then removes the item from the original userset
. The second examples creates a new set activeusersset
, and adds all users except for Bob to activeusers
.
""" Compliant Code Example """
userset = {'Alice', 'Bob', 'Charlie'}
print(f'Unmodified set: {userset}')
# Create a copy
for user in userset.copy():
if user == 'Bob':
userset.remove(user)
print(f'Modified set: {userset}')
# Create a new set
userset2 = {'Alice', 'Bob', 'Charlie'}
activeuserset = set()
for user in userset2:
if user != 'Bob':
activeuserset.add(user)
print(f'Modified set: {activeuserset}')
The requirement to use copy()
or deepcopy()
will vary depending on the problem that needs to be solved.
Pylint
version 2.9.6
can detect an issue with the noncompliant01.py
and nocompliant03.py
code but is unable to detect an issue with noncompliant02.py
.
Tool | Version | Checker | Description |
Pylint | 2.9.6 | W4701:modified-iterating-list | Iterated list userlist is being modified inside for loop body, consider iterating through a copy of it instead. |
Pylint | 2.9.6 | E4703:modified-iterating-set | Iterated set userset is being modified inside for loop body, iterate through a copy of it instead. |
[Python 3.9 2024] | 4.2. for Statements. Available from: https://docs.python.org/3.9/tutorial/controlflow.html#for-statements [Accessed 7 June 2024] |