Dlaczego random.shuffle zwraca None?

88

Dlaczego random.shufflepowraca Nonew Pythonie?

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

Jak uzyskać przetasowaną wartość zamiast None?

alvas
źródło
nie losowa wartość, ale losowe tasowanie listy.
alvas
3
Powiązane: funkcje sort () i reverse () nie działają
Martijn Pieters

Odpowiedzi:

157

random.shuffle()zmienia xlistę na miejscu .

Metody interfejsu API języka Python, które zmieniają strukturę lokalnie, generalnie zwracają None, a nie zmodyfikowaną strukturę danych.

Jeśli chcesz utworzyć nową losowo odtwarzaną listę na podstawie istniejącej, gdzie istniejąca lista jest utrzymywana w porządku, możesz użyć random.sample()z pełną długością danych wejściowych:

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

Możesz również użyć sorted()z random.random()jako klucz sortowania:

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

ale to wywołuje sortowanie (operacja O (NlogN)), podczas gdy próbkowanie do długości wejściowej wymaga tylko operacji O (N) (ten sam proces, który random.shuffle()jest używany, zastępując losowe wartości z kurczącej się puli).

Próbny:

>>> 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']
Martijn Pieters
źródło
2
Czy korzystanie z keyfunkcji o wartości losowej jest naprawdę gwarantowane? Niektóre algorytmy szybkiego sortowania zawodzą, jeśli porównania nie są spójne. Widzę, że działa to w obie strony, w zależności od implementacji (dekorowanie-sortowanie-undecorate będzie musiało zostać zastosowane keytylko raz na każdym elemencie, więc będzie dobrze zdefiniowane).
torek
2
@torek: Python używa decorate-sort-undecorate podczas sortowania z możliwością keywywołania. Więc tak, jest to gwarantowane, ponieważ każda wartość otrzymuje swój losowy klucz dokładnie raz.
Martijn Pieters
35

Ta metoda też działa.

import random
shuffled = random.sample(original, len(original))
Acemad
źródło
8

Według dokumentów :

Potasuj sekwencję x na miejscu. Opcjonalny argument random to 0-argumentowa funkcja zwracająca losową liczbę zmiennoprzecinkową z wartości [0.0, 1.0); domyślnie jest to funkcja random ().

>>> x = ['foo','bar','black','sheep']
>>> from random import shuffle
>>> shuffle(x)
>>> x
['bar', 'black', 'sheep', 'foo']
alecxe
źródło
6

Dlaczego tak naprawdę?

1. Wydajność

shufflemodyfikuje listę w miejscu. To jest miłe, ponieważ kopiowanie dużej listy byłoby czystym kosztem, jeśli nie potrzebujesz już oryginalnej listy.

2. Styl Pythonic

Zgodnie z zasadą stylu pythonowego „wyraźne jest lepsze niż niejawne” , zwrócenie listy byłoby złym pomysłem, ponieważ wówczas można by pomyśleć, że jest to nowa, chociaż w rzeczywistości tak nie jest.

Ale mi się to nie podoba!

Jeśli nie potrzebujesz nową listę, trzeba będzie napisać coś podobnego

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

co jest ładnie wyraźne. Jeśli często potrzebujesz tego idiomu, zawiń go w funkcję shuffled(zobacz sorted), która zwraca new_x.

Lutz Prechelt
źródło
2

Miałem chwilę aha z taką koncepcją:

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']
litepresence
źródło
Ponieważ python domyślnie wykonuje „kopiuj-by-wartości” zamiast pass-by-reference =) stackoverflow.com/a/986495/610569
alvas
2

Pythonowe API, które zmieniają strukturę, same zwracają None jako dane wyjściowe.

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

Wyjście: [1, 2, 3, 4, 5, 6, 7, 8]

from random import shuffle
print(shuffle(list))

Wyjście: brak

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

Wyjście: [7, 3, 2, 4, 5, 6, 1, 8]

user3467537
źródło
0

Możesz zwrócić potasowaną listę, korzystając z random.sample()wyjaśnień innych. Działa na zasadzie próbkowania k elementów z listy bez zastępowania . Jeśli więc na liście znajdują się zduplikowane elementy, zostaną one potraktowane wyjątkowo.

>>> 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]
devsaw
źródło