I’m new to Python and was driven crazy by this “feature” or bug.
Run the program below and could someone please explain the behaviour.
If you read the documentation for copy() it clearly states a shadow copy of a list is created and returned. I am using 3.11.0 and IDLE and for a list of simple elements copy() works as expected.
Hoever copy() does not work as expected with a list of lists. Instead copy() returns a reference to the original list.
print("Code that demonstrates how copy() works with a simple list of elements")
eTestList = [1,2,3,4]
print("Test case: ",eTestList)
print()
print("Create and modify a copy of the list")
NeweList = eTestList.copy()
# now change the new list and print the original and new list
NeweList[0]=0
print(" Original: ",eTestList)
print(" New : ",NeweList)
print("Worked as expected. Nothing crazy here")
print()
print()
print("Code that demonstrates how copy() does not work with list of lists")
lolTestList = [[1,0,0,0]]
lolTestList.append([2,0,0,0])
lolTestList.append([3,0,0,0])
print("Test case: ",lolTestList)
print()
print("Create and modify a copy of the list")
NewlolList = lolTestList.copy()
# now change the new list and print the original and new list
NewlolList[1][0] = 0
print(" Original: ",lolTestList)
print(" New : ",NewlolList)
print("Both lists are changed. Bad, very bad!!")
print("I think it should either work or fail but instead it appears copy() returns a reference to the original list")
>>> a = [[1], [2]]
>>> b = a
>>> a is b
True
>>> # a and b are the same object, so any changes made to the contents of one affect both.
>>> b[0] = 0
>>> assert a[0] == 0
Shallow copy:
>>> a = [[1], [2]]
>>> b = a.copy()
>>> a is b
False
>>> # a and b are different objects.
>>> b[0] = 5
>>> assert a[0] == 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
>>> b = a.copy()
>>> a[0] is b[0]
True
>>> # But a[0] and b[0] are still the same object.
>>> b[0][0] = 5
>>> assert a[0][0] == 5
Deep copy:
>>> from copy import deepcopy
>>> b = deepcopy(a)
>>> a[0] is b[0]
False
>>> # a[0] and b[0] are no longer the same object.
>>> b[0][0] = 5
>>> assert a[0][0] == 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
Yep, I think every python developer gets bit by this at some point. Another one you’re likely to run into sooner or later is late binding closures:
>>> funcs = [lambda: print(i) for i in range(2)]
>>> for f in funcs:
>>> f()