1

I've noticed an odd idiosynracy when trying to modify string elements of a list. For example:

ls = ['a','b']

for elem in ls : 
  elem += '!'
  print(elem)
print(ls)


# Output: 
# a!
# b!
# ['a','b']
for i in range(len(ls)) :    
  ls[i] += '!'
print(ls)

# Output:
# ['a!','b!']

Why can I only modify strings in a list when using an indexed range? I just checked and it is the same when trying to modify integers.

u-lx
  • 11
  • 2
  • 6
    `elem` isn't related to your `list` in any meaningful way. It's exactly the same as if you had done `elem = ls[0]` `elem += '!'`. You may find it helpful to read through [this](https://nedbatchelder.com/text/names1.html). – Axe319 Jan 06 '23 at 01:34
  • 4
    You are not modifying the contents of the list, as they are immutable. You are creating new objects that only exist within the loop. If you do not put the new objetcs into the list, then the list will keep containing the original objects. – Mike Scotty Jan 06 '23 at 01:34
  • Because **nowhere** are you modifying *any string or any integer*. Indeed, both strings and integers are **immutable** – juanpa.arrivillaga Jan 06 '23 at 04:54

3 Answers3

0
for elem in ls : 
    elem += '!'

In this loop, elem is its own separate variable. It has no connection back to the list it came from. Modifying it will not affect the list.

John Gordon
  • 29,573
  • 7
  • 33
  • 58
  • 3
    This doesn't explain how to solve the problem, it just restates what the question says. There's almost certain many duplicates you could link to. – Barmar Jan 06 '23 at 01:57
0

Lists in python are not an immutable object, however, for loops variables are set and used only within the loop itself, with no correlation with the list being looped over.

However, in the second snippet you're directly modifying the list as you would normally do.

for i in range(0,10):
    i+=2; print(i)
#outputs 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

If for example you tried to modify the i variable like in the script above it wouldn't affect the loop at all because it would only be modified in the current step and then completely discarded

  • "for loops variables are set and used only within the loop itself," that isn't correct exactly. loop variables belong exist after the loop terminates, for loops do not have their own scope – juanpa.arrivillaga Jan 06 '23 at 04:52
0

The fundamental issue here is that you aren't modifying any objects at all. Consider the following code:

>>> x = 0
>>> y = x
>>> x is y
True
>>> x += 1
>>> x, y
(1, 0)
>>> x is y
False

The operation += creates a new int object and assigns it back to the variable on the left. And because assignment doesn't mutate, no changes have been made to any of the objects in the list or the list itself. Indeed, int objects and str objects don't expose any mutator methods, i.e. int and str objects are immutable.

There is a subtlety here, though. The += operator (which is an augmented assignment operator) will mutate mutable objects (this is a convention, although for built-in types it is guaranteed, but you can implement the augmented assignment operators to do as you please, but you should probably stick to the convention). So for example, another list inside your list would be mutable (since lists are mutable):

>>> x = []
>>> y = x
>>> x is y
True
>>> x += [1]
>>> x, y
([1], [1])
>>> x is y
True

But note, assignment by itself never mutates:

>>> x = []
>>> y = x
>>> x is y
True
>>> x = x + [1]
>>> x, y
([1], [])
>>> x is y
False

So if you had mutated the object:

>>> data = [[1], [2], [3]]
>>> for sub in data:
...    sub += [99]
...
>>> data
[[1, 99], [2, 99], [3, 99]]

Or using another mutator method:

>>> data = [[1], [2], [3]]
>>> for sub in data:
...     sub.pop()
...
1
2
3
>>> sub
[]

Then the changes would have been reflected in the list.

But simple assignment by itself never mutates:

>>> data = [[1], [2], [3]]
>>> for sub in data:
...     sub = sub + [99] # no mutator methods being used
...
>>> data
[[1], [2], [3]]

So, something you must fundamentally understand is when you are working with mutable or immutable objects, and if you are working with mutable object, if you are using mutator methods or not.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172