Почему random.shuffle возвращает None?

88

Почему random.shuffleвозвращается Noneв Python?

>>> x = ['foo','bar','black','sheep']
>>> from random import shuffle
>>> print shuffle(x)
None

Как мне получить перетасованное значение вместо None?

альвас
источник
не случайное значение, а случайное перемешивание списка.
alvas
3
По теме: функции sort () и reverse () не работают
Martijn Pieters

Ответы:

157

random.shuffle()меняет xсписок на место .

Методы Python API, которые изменяют структуру на месте, обычно возвращают None, а не измененную структуру данных.

Если вы хотите создать новый случайным образом перетасованный список на основе существующего, где существующий список сохраняется в порядке, вы можете использовать random.sample()с полной длиной ввода:

x = ['foo', 'bar', 'black', 'sheep']
random.sample(x, len(x))     

Вы также можете использовать sorted()with random.random()для ключа сортировки:

shuffled = sorted(x, key=lambda k: random.random())

но это вызывает сортировку (операция O (NlogN)), в то время как выборка для входной длины занимает только O (N) операций (тот же процесс, что random.shuffle()и используется, замена случайных значений из сужающегося пула).

Демо:

>>> import random
>>> x = ['foo', 'bar', 'black', 'sheep']
>>> random.sample(x, len(x))
['bar', 'sheep', 'black', 'foo']
>>> sorted(x, key=lambda k: random.random())
['sheep', 'foo', 'black', 'bar']
>>> x
['foo', 'bar', 'black', 'sheep']
Мартейн Питерс
источник
2
Действительно ли keyгарантировано использование функции со случайными значениями ? Некоторые алгоритмы быстрой сортировки не работают, если сравнения не являются самосогласованными. Я вижу, что это работает в любом случае, в зависимости от реализации (decorate-sort-undecorate нужно будет применить keyтолько один раз к каждому элементу, поэтому он будет четко определен).
torek
2
@torek: Python использует decorate-sort-undecorate при сортировке с помощью keyвызываемого объекта. Так что да, это гарантировано, поскольку каждому значению присваивается случайный ключ ровно один раз.
Мартейн Питерс
35

Этот метод тоже работает.

import random
shuffled = random.sample(original, len(original))
Acemad
источник
8

Согласно документам :

Перемешайте последовательность x на место. Необязательный аргумент random - это функция с 0 аргументами, возвращающая случайное число с плавающей запятой в [0.0, 1.0); по умолчанию это функция random ().

>>> x = ['foo','bar','black','sheep']
>>> from random import shuffle
>>> shuffle(x)
>>> x
['bar', 'black', 'sheep', 'foo']
алексе
источник
6

Почему на самом деле?

1. Эффективность

shuffleизменяет список на месте. Это хорошо, потому что копирование большого списка было бы чистыми накладными расходами, если вам больше не нужен исходный список.

2. Питонический стиль

В соответствии с «явно лучше , чем неявное» принцип вещий стиля , возвращая список будет плохая идея, потому что тогда можно было бы подумать , что это новый один , хотя на самом деле это не так .

Но мне это не нравится!

Если вам действительно нужен свежий список, вам нужно будет написать что-то вроде

new_x = list(x)  # make a copy
random.shuffle(new_x)

что красиво и ясно. Если вам часто нужна эта идиома, оберните ее в функцию shuffled(см. sorted), Которая возвращает new_x.

Лутц Прешельт
источник
2

У меня был момент аха с такой концепцией:

from random import shuffle
x = ['foo','black','sheep'] #original list
y = list(x) # an independent copy of the original
for i in range(5):
    print shuffle(y) # shuffles the original "in place" prints "None" return
    print x,y #prints original, and shuffled independent copy

>>>
None
['foo', 'black', 'sheep'] ['foo', 'black', 'sheep']
None
['foo', 'black', 'sheep'] ['black', 'foo', 'sheep']
None
['foo', 'black', 'sheep'] ['sheep', 'black', 'foo']
None
['foo', 'black', 'sheep'] ['black', 'foo', 'sheep']
None
['foo', 'black', 'sheep'] ['sheep', 'black', 'foo']
легкое присутствие
источник
Потому что питон делает «копировать-на-значения» по умолчанию вместо передачи по ссылке =) stackoverflow.com/a/986495/610569
Alvas
2

API-интерфейсы Python, которые изменяют структуру на месте, сами возвращают None в качестве вывода.

list = [1,2,3,4,5,6,7,8]
print(list)

Вывод: [1, 2, 3, 4, 5, 6, 7, 8]

from random import shuffle
print(shuffle(list))

Выход: нет

from random import sample
print(sample(list, len(list)))

Вывод: [7, 3, 2, 4, 5, 6, 1, 8]

user3467537
источник
0

Вы можете вернуть перемешанный список, используя, random.sample()как объяснили другие. Он работает, выбирая k элементов из списка без замены . Поэтому, если в вашем списке есть повторяющиеся элементы, они будут обрабатываться однозначно.

>>> l = [1,4,5,3,5]
>>> random.sample(l,len(l))
[4, 5, 5, 3, 1]
>>> random.sample(l,len(l)-1)
[4, 1, 5, 3]
>>> random.sample(l,len(l)-1)
[3, 5, 5, 1]
пила
источник