4

I am looping through a list of tuples and, each iteration, I am appending some extra elements to the current loop variable, then performing an action with the new list.

The temptation is to modify the loop variable to include the extra elements, then do something with it.

Consider the following code snippet:

required_part = (0, 4)
optional_part = [(1, 2), (1, 3), (2,3)]

for x in optional_part:
    x += required_part
    x = sorted(x)
    print(x)

But something about mutating the loop variable during a loop makes me feel uneasy.

Are there any situations when mutating the loop variable will produce unexpected results or can I just stop worrying?

Note: there seems to be plenty of discussion about mutating the iterable. This question is rather about mutating the loop variable

LondonRob
  • 73,083
  • 37
  • 144
  • 201
  • 3
    this is perfectly OK, you're reassigning `x`. That doesn't change `optional_part` list, of course. – Jean-François Fabre Sep 29 '17 at 12:01
  • You're just re-assigning `x`, and at the start of each iteration `x` is re-assigned to the next value in the loop. There's not mutation/modification involved (tuples are immutable anyway) – Chris_Rands Sep 29 '17 at 12:05
  • Knowing how to formulate this question is why I asked [this other question](https://stackoverflow.com/questions/46452452/what-is-the-counter-variable-in-a-python-loop-called). Preparation! – LondonRob Sep 29 '17 at 12:06
  • Never worry about mutating tuples :) Python won't let you – user2390182 Sep 29 '17 at 12:07
  • @schwobaseggl Yes, I did think about this. Maybe I should've made the example elements lists instead of tuples. But I thought maybe people would say "just use tuples" or something so I left it as it was. – LondonRob Sep 29 '17 at 12:10

3 Answers3

4

As long as you're not changing the collection itself, it doesn't matter. The problem only arises when you try to add/delete from the very collection, you're iterating over.

# Say something like this

for x in optional_part:
    optional_part.remove(x)
hspandher
  • 15,934
  • 2
  • 32
  • 45
  • And even then, it's a perfectly normal thing to modify the collection too; for e.g. looping `while` a collection is non-empty and popping elements as you go, and many other examples. – alkasm Sep 29 '17 at 12:03
  • @AlexanderReynolds however, the code I mentioned wouldn't behave as expected, because of the same. It is fine in case of `while` loops, but not iteration. – hspandher Sep 29 '17 at 12:04
  • 1
    Indeed, I just meant to address OP's worry about modifying variables and collections...both are used normally in programming, but just make sure it's a sensible thing to do. – alkasm Sep 29 '17 at 12:05
3

While most has been said in the comments and the other answer, it might be worth noting that the += operator does not always perform a reassignment. It is just the way tuple implements it (which it has to due its immutability). Tuples and lists implement it rather differently:

Lists do mutate:

a = x = [5, 6]
a += [7]
a
# [5, 6, 7]
x
# [5, 6, 7]

Tuples don't:

a = x = (5, 6)
a += (7,)
a
# (5, 6, 7)
x
# (5, 6)
user2390182
  • 72,016
  • 6
  • 67
  • 89
  • it's not an implementation difference, `tuple` is immutable: using `+=` changes its reference or it would break guaranteed immutability. At this point ids of `a` and `x` differ (like strings) – Jean-François Fabre Sep 29 '17 at 12:19
  • @Jean-FrançoisFabre `list` could still impement the `__radd__` differently. You have `append` and `extend` after all. Also, I think, many have the illusion that `a += b` is equivalent to `a = a + b` which is not the case for lists, which I find noteworthy. – user2390182 Sep 29 '17 at 12:23
  • Yes, but it would be a bad thing to implement them differenly because of the quadratic complexity of the data copy. – Jean-François Fabre Sep 29 '17 at 12:36
  • @Jean-FrançoisFabre Quadratic, i'm not sure. For `m += n` the time complexity should be `O(N)` in the current implementation and `O(M+N)` if there was a reassignment with new object, right? – user2390182 Sep 29 '17 at 12:38
2

The question is, what is the loop variable bound to? If that value is mutable, you can change the contents of the list, though not the list itself.

>>> lst = [[1, 2], [3, 4]]
>>> for x in lst:
...  x += [5]
...
>>> print lst
[[1, 2, 5], [3, 4, 5]]

lst is still the same list of two elements, both those elements have been changed.

If the elements aren't mutable, then nothing changes.

>>> lst = [1,2,3]
>>> for x in lst:
...   x += 5
...
>>> print lst
[1, 2, 3]
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Yes of course! The mutability of the elements is the crucial part of this. I *knew* there was a reason to be nervous! – LondonRob Sep 29 '17 at 12:32