Pourquoi random.shuffle renvoie None?

88

Pourquoi random.shufflerevient Noneen Python?

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

Comment obtenir la valeur mélangée au lieu de None?

alvas
la source
pas une valeur aléatoire mais un mélange aléatoire de la liste.
alvas
3
Connexes: les fonctions sort () et reverse () ne fonctionnent pas
Martijn Pieters

Réponses:

157

random.shuffle()modifie la xliste en place .

Les méthodes d'API Python qui modifient une structure sur place renvoient généralement None, et non la structure de données modifiée.

Si vous souhaitez créer une nouvelle liste aléatoire basée sur une liste existante, où la liste existante est conservée dans l'ordre, vous pouvez utiliser random.sample()avec toute la longueur de l'entrée:

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

Vous pouvez également utiliser sorted()avec random.random()pour une clé de tri:

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

mais cela invoque le tri (une opération O (NlogN)), tandis que l'échantillonnage à la longueur d'entrée ne prend que des opérations O (N) (le même processus que celui random.shuffle()utilisé, l'échange de valeurs aléatoires à partir d'un pool de réduction).

Démo:

>>> 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
la source
2
L'utilisation d'une keyfonction à valeur aléatoire est-elle vraiment garantie? Certains algorithmes de tri rapide tombent si les comparaisons ne sont pas cohérentes. Je peux voir que cela fonctionne de toute façon, en fonction de l'implémentation (decorate-sort-undecorate n'aura besoin de s'appliquer keyqu'une seule fois sur chaque élément donc sera bien défini).
torek
2
@torek: Python utilise decorate-sort-undecorate lors du tri avec un keyappelable. Donc oui, c'est garanti car chaque valeur reçoit sa clé aléatoire exactement une fois.
Martijn Pieters
35

Cette méthode fonctionne aussi.

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

Selon la documentation :

Mélangez la séquence x en place. L'argument optionnel random est une fonction à 0 argument retournant un flottant aléatoire dans [0.0, 1.0); par défaut, c'est la fonction random ().

>>> x = ['foo','bar','black','sheep']
>>> from random import shuffle
>>> shuffle(x)
>>> x
['bar', 'black', 'sheep', 'foo']
alecxe
la source
6

Pourquoi, vraiment?

1. Efficacité

shufflemodifie la liste en place. C'est bien, car copier une grande liste serait une surcharge si vous n'avez plus besoin de la liste d'origine.

2. Style pythonique

Selon le « explicite est mieux que implicite » principe de style de pythonique , retournant la liste serait une mauvaise idée, car alors on pourrait penser qu'il est un nouveau bien en fait , il n'est pas.

Mais je n'aime pas ça comme ça!

Si vous avez besoin d' une nouvelle liste, vous devrez écrire quelque chose comme

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

ce qui est bien explicite. Si vous avez fréquemment besoin de cet idiome, enveloppez-le dans une fonction shuffled(voir sorted) qui renvoie new_x.

Lutz Prechelt
la source
2

J'ai eu mon moment aha avec ce concept comme celui-ci:

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
la source
Parce que python fait "copie-par-valeurs" par défaut au lieu de pass-by-reference =) stackoverflow.com/a/986495/610569
alvas
2

Les API Python qui modifient la structure en place elles-mêmes retournent None en sortie.

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

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

from random import shuffle
print(shuffle(list))

Sortie: Aucun

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

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

user3467537
la source
0

Vous pouvez retourner la liste aléatoire en utilisant random.sample()comme expliqué par d'autres. Il fonctionne en échantillonnant k éléments de la liste sans remplacement . Donc, s'il y a des éléments en double dans votre liste, ils seront traités de manière unique.

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