Python 在通过 for 循环完成迭代后重新定义随机数列表的整个列表



我已经使用Python几年了,不知道为什么它以以下方式运行。但话虽如此,我认为我从未尝试过以这种特殊方式处理随机数。请随时检查您的 python(我使用 3.5(的行为方式是否相同。

目标:我想生成一个列表列表,其中每个嵌套列表包含相同的值,但顺序不同。

我有什么代码

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

因此,我声明了一个包含所需值的选项列表。然后我使用 random.shuffle(( 来混淆列表。然后将其追加到主列表中。但是当我去检查结果时,每个嵌套列表都有相同的有序数字列表......

>>> 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]
>>>

我原本有比上面更复杂的代码,但是因为我以前没有在python中广泛使用伪随机数,所以我认为我没有完全理解随机方法。所以我不断使代码越来越简单。但结果是一样的。所以我终于发现 for 循环按预期工作,直到它完成迭代......

带有引导注释的代码:

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]))

结果:

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]

因此,第一个输出部分反映了每个追加的嵌套列表。第二个输出块反映相同的内容,但在 for 循环完成其迭代之后。

我不明白为什么它会这样。出于某种原因,每个嵌套列表都更改为最后一个附加列表,但必须在 for 循环完成时或之后发生。

知道为什么吗?

列表是可变的。这意味着即使他们有一个身份,他们也可以有很多状态。您尝试做的是将相同的列表附加几次不同的时间,期望它们的当前状态保持这种状态。但实际上,每次重新洗牌时,该列表的所有实例都会以相同的方式重新洗牌。改变一个会改变它们。你想要的是该列表的副本,一个你永远不会更改的副本,所以它看起来会冻结:

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

您每次都反复洗牌相同的列表对象并将其附加到li列表中。请注意子列表的 ID 都是相同的:

>>> 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]

这与可变默认参数"gotcha"是相同的问题。

您可以使用副本解决此问题:

>>> 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

请注意,如果opts很大或其元素是大型对象,则最终可能会使用大量内存。如果这是一个问题,请考虑编写自己的生成器(也许洗牌索引列表?

无论如何,这解释了您所看到的行为。

您一遍又一遍地添加对同一列表的引用。

您会看到最后一次随机播放的结果在所有实例中镜像。

最新更新