0

In the python 3 documentation, str.replace is described as:

str.replace(old, new[, count])

Return a copy of the string with all occurrences of substring old replaced by new. If the optional argument count is given, only the first count occurrences are replaced.

According to this, the code

old = [['z','b']]
new = []
new.append(old[0])

print(old == new)

new[0][0] = old[0][0].replace('z','a')

print([old,new,old==new])

should return

True
[[['z', 'b']], [['a', 'b']], False]

However, in both python 2 and 3 it actually returns:

True
[[['a', 'b']], [['a', 'b']], True]

meaning that old has been modified from [['z','b']] to [['a', 'b']] even though the documentation implies it shouldn't. However, it's not that the documentation was poorly worded. The value of old is not changed every time. It depends on how we define the variable new. If we define new in a different but (seemingly) equivalent way:

old = [['z','b']]
new = [['z','b']]

print(old == new)

new[0][0] = old[0][0].replace('z','a')

print([old,new,old==new])

we get the output:

True
[[['z', 'b']], [['a', 'b']], False]

which is what we expected in the first place. In both cases, before the str.replace is used, old == new evaluates to True. Yet, depending on the method used to set the variable new to [['z','b']], str.replace changes the value of old.

How does Python "remember" how the variable new is defined, and change its output accordingly? How does Python know that there is something different between the two equivalent new variables? Regardless, why is the value of old changing at all? Shouldn't str.replace just return a modified copy of old instead of actually changing it?

  • You may find this article helpful: [Facts and myths about Python names and values](http://nedbatchelder.com/text/names.html), which was written by SO veteran Ned Batchelder. – PM 2Ring Nov 12 '17 at 09:16
  • There is nothing inconsistent here, and `replace` does not ever modify the string. – Daniel Roseman Nov 12 '17 at 09:18
  • Bear in mind that strings are immutable and cannot be changed, whereas list objects _are_ mutable. In your 1st example, you make `new[0]` the same list object as `old[0]`, it's not a copy. In the 2nd example, The `new[0]` list is a separate list object that just happens to have the same value as the `old[0]` list object. – PM 2Ring Nov 12 '17 at 09:19
  • 1
    Also refer to https://stackoverflow.com/questions/240178/list-of-lists-changes-reflected-across-sublists-unexpectedly - which is the more accurate duplicate. – metatoaster Nov 12 '17 at 09:20

1 Answers1

0

You are not .append(..)ing the contents of old but a reference of old - use .copy() or .deepcopy() on old - then you have two distinct values that do not reference the same object.

Essentially new and old in your 1st usage point to the same data, in your second example they point to different data.

See How to clone or copy a list?

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69