静かなる名辞

pythonと読書

【python】random.shuffleについて

問題のコード

 こういう奴。

import random
lst = list(range(10))
shuffled = random.shuffle(lst)
for x in shuffled:
    print(x)

 いかにも人畜無害な雰囲気のコードですが、実行すると「TypeError: 'NoneType' object is not iterable」を吐いてくれます。

正しいやり方
import random
lst = list(range(10))
random.shuffle(lst)
for x in lst:
    print(x)
"""
=>
1
9
5
3
7
0
4
2
8
6
"""

 random.shuffleはいわゆる「破壊的操作」で、リストの参照を受け取って(pythonのリストは参照渡しだからね)、その参照先をシャッフルします。Cかよ。
 ともかく、こいつは関数じゃないのです。pythonがうたう『マルチパラダイム言語』の中にはオールドファッションな手続き型も入ってると思うんで、こういうものが標準ライブラリの中にいても怒ってはいけません。僕は思わず画面に湯呑みを投げつけそうになりました。

でも元のリスト破壊されたら困るときもあるじゃん

 random.shuffleは参照先をそのままいじるので、元々どんな順番で要素が並んでいたのかという情報は残してくれません。元のリストもとっておきたいときは自前でやる必要があります。
 色々なやり方があると思いますが、とりあえずlist()しとけば別オブジェクトになるのでそれで良いんじゃないかな。

import random
lst = list(range(10))
shuffled = list(lst); random.shuffle(shuffled)
for x, y in zip(lst, shuffled):
    print(x, y)
"""
=>
0 0
1 3
2 8
3 6
4 4
5 9
6 1
7 7
8 5
9 2

"""

 ひとつづきの処理であることを強調するために、セミコロン使って同じ行に入れてみました。ちなみに、random.shuffle(list(lst))とかすると、random.shuffleさんはちゃんと健気に動いてくれるものの、結果を取り出す手段がなくなって死にます(オブジェクトが)。