Я работал с 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.

Есть идеи, почему?

2
ksgWXfan 20 Авг 2018 в 21:15

3 ответа

Лучший ответ

Списки изменчивы. Это означает, что, хотя они имеют одну идентичность, они могут иметь много состояний . То, что вы пытаетесь сделать, это добавлять один и тот же список несколько раз, ожидая, что их текущие состояния останутся такими же. Но на самом деле, каждый раз, когда вы переставляете его, все экземпляры этого списка переставляют один и тот же путь. Изменение одного меняет их всех. Вам нужна копия этого списка, которую вы никогда не измените, поэтому она будет выглядеть замороженной:

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
4
Levi Lesches 20 Авг 2018 в 18:22

Вы несколько раз перетасовываете один и тот же объект списка и добавляете его в список li каждый раз. Обратите внимание, что идентификаторы подсписков одинаковы:

>>> 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 имеет большой размер или его элементы являются большими объектами, это может привести к использованию большого количества памяти. Посмотрите на написание своего собственного генератора (возможно, перетасовывает список индексов?), Если это проблема.

В любом случае это объясняет поведение, которое вы видите.

1
Scott Colby 20 Авг 2018 в 18:23

Вы добавляете ссылки в один и тот же список снова и снова.

Вы видите результат последнего воспроизведения в случайном порядке во всех случаях.

0
bohrax 20 Авг 2018 в 18:20
51936434