I started to make a program that reorders the letters in a string
in different ways to practice some Python. I noticed that when using the .append()
method in a for loop, only the last iteration was getting appended again and again. I searched for an explanation but all I could
find was as solution to my issue but no good explanation to why it works.
My original code is:
def foo(word):
wordlist = list(word)
wordlist_copy = wordlist.copy()
combinations = []
for char in wordlist:
for i,let in enumerate(wordlist_copy):
wordlist_copy[i] = char
print(wordlist_copy)
combinations.append(wordlist_copy)
return combinations
For the string 'ba' I expected what the print statement is displaying to be stored in combinations
.
This being [['b', 'a']['b', 'b']['a', 'b']['a', 'a']]
.
However what I got was
[['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a']]
The solution I found was to change line 11: combinations.append(wordlist_copy)
to combinations.append(list(wordlist_copy))
.
Why is it that using the list
method fixes my issue? I am already appending the wordlist_copy
list on each iteration of the loop, so I can't understand how the method would do anything special here.
wordlist_copy
is a named reference to a list. combinations.append(wordlist_copy)
appends another reference to that list to the combinations
list. Now you have multiple references to the same list. Change a value through one reference and all other references will see that same change because it's only one underlying object. Print out the references in combinations
while your program is running and you'll see the problem:
def foo(word):
wordlist = list(word)
wordlist_copy = wordlist.copy()
combinations = []
for char in wordlist:
for i,let in enumerate(wordlist_copy):
wordlist_copy[i] = char
print(wordlist_copy)
combinations.append(wordlist_copy)
print("----", "ITERATION", i, "----")
for c in combinations:
print(id(c), c)
return combinations
foo("ab")
prints
['a', 'b']
---- ITERATION 0 ----
139817235422848 ['a', 'b']
['a', 'a']
---- ITERATION 1 ----
139817235422848 ['a', 'a']
139817235422848 ['a', 'a']
['b', 'a']
---- ITERATION 0 ----
139817235422848 ['b', 'a']
139817235422848 ['b', 'a']
139817235422848 ['b', 'a']
['b', 'b']
---- ITERATION 1 ----
139817235422848 ['b', 'b']
139817235422848 ['b', 'b']
139817235422848 ['b', 'b']
139817235422848 ['b', 'b']
By that final iteration, you have 4 references to a single list.
Your code keeps appending a reference to the single wordlist_copy
list to the combinations
list.
Since you haven't make new copies for combinations
, changes to wordlist_copy
are seen by all of the references in combinations
.
list(wordlist_copy)
fixes the problem because it creates a new list which to add to the list. It does the same thing as wordlist_copy.copy()
.