Por que random.shuffle retorna Nenhum?

88

Por que está random.shuffleretornando Noneem Python?

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

Como obtenho o valor embaralhado em vez de None?

Alvas
fonte
não um valor aleatório, mas um embaralhamento aleatório da lista.
alvas
3
Relacionado: as funções sort () e reverse () não funcionam
Martijn Pieters

Respostas:

157

random.shuffle()muda a xlista no local .

Os métodos da API Python que alteram uma estrutura local geralmente retornam None, não a estrutura de dados modificada.

Se você quiser criar uma nova lista embaralhada aleatoriamente com base em uma existente, em que a lista existente é mantida em ordem, você pode usar random.sample()com o comprimento total da entrada:

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

Você também pode usar sorted()com random.random()para uma chave de classificação:

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

mas isso invoca a classificação (uma operação O (NlogN)), enquanto a amostragem para o comprimento de entrada leva apenas O (N) operações (o mesmo processo que random.shuffle()é usado, trocando valores aleatórios de um pool de encolhimento).

Demo:

>>> 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
fonte
2
O uso de uma keyfunção com valor aleatório é realmente garantido? Alguns algoritmos de classificação rápida falham se as comparações não forem autoconsistentes. Eu posso ver isso funcionando de qualquer maneira, dependendo da implementação (decorate-sort-undecorate só precisará ser aplicado keyuma vez em cada elemento, portanto, será bem definido).
Torek
2
@torek: Python usa decorate-sort-undecorate ao classificar com um keychamável. Portanto, sim, é garantido que cada valor recebe sua chave aleatória exatamente uma vez.
Martijn Pieters
35

Este método também funciona.

import random
shuffled = random.sample(original, len(original))
Acemad
fonte
8

De acordo com a documentação :

Embaralhe a sequência x no lugar. O argumento opcional random é uma função de 0 argumento que retorna um float aleatório em [0.0, 1.0); por padrão, esta é a função random ().

>>> x = ['foo','bar','black','sheep']
>>> from random import shuffle
>>> shuffle(x)
>>> x
['bar', 'black', 'sheep', 'foo']
Alecxe
fonte
6

Por que, realmente?

1. Eficiência

shufflemodifica a lista no local. Isso é bom, porque copiar uma lista grande seria pura sobrecarga se você não precisar mais da lista original.

2. Estilo pitônico

De acordo com o princípio "explícito é melhor do que implícito" do estilo pythônico , devolver a lista seria uma má ideia, porque então poderíamos pensar que é uma lista nova, embora na verdade não seja.

Mas eu não gosto assim!

Se você não precisa de uma nova lista, você terá que escrever algo como

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

o que é bem explícito. Se você precisa desse idioma com freqüência, envolva-o em uma função shuffled(consulte sortedRecursos) que retorna new_x.

Lutz Prechelt
fonte
2

Eu tive meu momento aha com este conceito assim:

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']
litepresença
fonte
Porque python faz "cópia por valores" por padrão em vez de passar por referência =) stackoverflow.com/a/986495/610569
alvas
2

APIs Python que mudam a estrutura no local, retornam None como saída.

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

Resultado: [1, 2, 3, 4, 5, 6, 7, 8]

from random import shuffle
print(shuffle(list))

Resultado: Nenhum

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

Resultado: [7, 3, 2, 4, 5, 6, 1, 8]

user3467537
fonte
0

Você pode retornar a lista embaralhada usando random.sample()conforme explicado por outros. Ele funciona por amostragem de k elementos da lista sem substituição . Portanto, se houver elementos duplicados em sua lista, eles serão tratados de forma exclusiva.

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