Reputation: 23
I've worked with Python for a few years now and do not know why it's behaving in the following manner. But that said, I don't think I've ever attempted to work with random numbers in this particular way. Please feel free to check if your python (I use 3.5) is acting the same way.
Target: I want to produce a list of lists where each nested list contains the same values but in different order.
What code I have:
import random
# Define number slots
opts = [1,2,3,4,5]
li = []
for x in range(len(opts)):
random.shuffle(opts) # Jumbles the options list
li.append(opts) # Append that list to the parent list
So, I declare a list of options containing the values that I want. Then I use random.shuffle() to mix-up the list. It is then appended to the master list. But when I go to inspect the results, each nested list has the same ordered list of numbers...
>>> for each in li:
print(each)
[5, 4, 2, 1, 3]
[5, 4, 2, 1, 3]
[5, 4, 2, 1, 3]
[5, 4, 2, 1, 3]
[5, 4, 2, 1, 3]
>>>
I originally had code more complex than above, but Because I hadn't worked with pseudo-random numbers in python extensively before, I thought I didn't fully understand the random method. So I kept making the code simpler and simpler. But the outcome was the same. So I finally figured out that the for loop worked as intended until it finished iterating...
import random
# Define number slots
opts = [1,2,3,4,5]
li = []
print("Now defining the list: \n")
for x in range(len(opts)):
random.shuffle(opts) # Jumble the options list
li.append(opts) # Append that list
print("li[{}] = {}".format(x,li[x])) # Outputs list associated with index
print("\nNow print each index:")
for y in range(len(li)):
print("li[{}] = {}".format(y,li[y]))
Results:
Now defining the list:
li[0] = [3, 4, 2, 5, 1]
li[1] = [2, 1, 5, 3, 4]
li[2] = [1, 5, 2, 3, 4]
li[3] = [5, 1, 2, 4, 3]
li[4] = [2, 1, 5, 3, 4]
Now print each index:
li[0] = [2, 1, 5, 3, 4]
li[1] = [2, 1, 5, 3, 4]
li[2] = [2, 1, 5, 3, 4]
li[3] = [2, 1, 5, 3, 4]
li[4] = [2, 1, 5, 3, 4]
So, the first output section reflects each appended nested list. The second block of output reflects the same, but after the for-loop completes its iteration.
I'm at a loss why it's behaving this way. For some reason, each nested list is changed to the last appended list but must happen upon or after completion of the for-loop.
Any ideas why?
Upvotes: 2
Views: 75
Reputation: 1430
You are shuffling the same list object repeatedly and appending it to the li
list every time. Notice how the ID's of the sublists are all the same:
>>> import random
>>> opts = [1,2,3,4,5]
>>> li = []
>>> for x in range(len(opts)):
... random.shuffle(opts)
... li.append(opts)
...
>>> li
[[1, 3, 4, 5, 2], [1, 3, 4, 5, 2], [1, 3, 4, 5, 2], [1, 3, 4, 5, 2], [1, 3, 4, 5, 2]]
>>> [id(item) for item in li]
[4392262856, 4392262856, 4392262856, 4392262856, 4392262856]
This is the same problem as the mutable default argument "gotcha".
You could solve this problem with a copy:
>>> import random
>>> opts = [1,2,3,4,5]
>>> li = []
>>> for x in range(len(opts)):
... new_list = opts.copy()
... random.shuffle(new_list)
... li.append(new_list)
...
>>> li
[[4, 1, 2, 5, 3], [3, 5, 2, 4, 1], [3, 2, 1, 5, 4], [5, 2, 4, 1, 3], [1, 3, 5, 4, 2]]
>>> [id(item) for item in li]
[4392411016, 4392262856, 4392410952, 4392399112, 4392399048]
>>> id(opts)
4392411080
Beware that if opts
is large or its elements are large objects, this could end up using a lot of memory. Look into writing your own generator (maybe shuffling a list of indices?) if this is an issue.
In any case, this explains the behavior you're seeing.
Upvotes: 1
Reputation: 1601
Lists are mutable. That means that even though they have one identity, they can have many states. What you attempt to do is append the same list a few different times, expecting their current states to stay that way. But in reality, every time you re-shuffle it, all instances of that list re-shuffle the same way. Changing one changes them all. What you want is a copy of that list, one that you never change, so it'll appear frozen:
for x in range(len(opts)):
copy = opts.copy() # make a copy
random.shuffle(copy) # shuffle only the copy
li.append(copy) # append only the copy
Upvotes: 4
Reputation: 1091
You are adding references to the same list over and over.
You see the result of the last shuffle mirrored in all instances.
Upvotes: 0