pythonで重複の無いリスト

例えばテキストデータを舐めて単語の種類数を求めたいときとか、重複の無いリストが必要になる。
今まで、何も考えずに以下のように書いてた。

良くない例

>>> a = [1,2,2,1,3,4,1,2,3]
>>> b = []
>>> for c in a: # aの中身をcで回す
...   if c not in b: # cがbに入ってなければ追加
...     b.append(c)
...
>>> b # 重複の無いリスト
[1, 2, 3, 4]

setを使う

ぶっちゃけsetを使った方がコードが短かくて済む。そして速い。
ただし、リストと違って順序は保存されないので注意。

>>> a # aの中身を確認
[1, 2, 2, 1, 3, 4, 1, 2, 3]
>>> b = set(a)
>>> b # 上と同じ
{1, 2, 3, 4}

しかし、この書き方をするなら一度aにすべての要素を突っ込まないと行けない。
最初にあげた単語を舐める例だと、テキストデータに出現しうるすべての単語を、一度aに突っ込まないといけない。
メモリ的にしんどい時は以下のように書くのが良い。
遅くなるけどリストに比べたらまだ速い。

>>> a
[1, 2, 2, 1, 3, 4, 1, 2, 3]
>>> b = set() # 空のsetを用意
>>> for c in a:
...   b.add(c) # 重複しないように追加してくれる
...
>>> b
{1, 2, 3, 4}

速度比較

比較用コード

>>> # listで回す
>>> def testList(arr):
...   st = time.time()
...   neoarr = []
...   for a in arr:
...     if a not in neoarr:
...       neoarr.append(a)
...   print(time.time()-st)
...   return neoarr
...
>>> # listのset
>>> def testSet1(arr):
...   st = time.time()
...   neoarr = set(arr)
...   print(time.time()-st)
...   return neoarr
...
>>> # listを回してsetにadd
>>> def testSet2(arr):
...   st = time.time()
...   neoarr = set()
...   for a in arr:
...     neoarr.add(a)
...   print(time.time()-st)
...   return neoarr

0から9までの整数をランダムに100万生成して、重複の無いリストを求める。

# listで回す
>>> testList([random.randint(0,9) for _ in range(1000000)])
0.14845705032348633
[3, 5, 8, 1, 4, 6, 9, 7, 0, 2]
>>> testList([random.randint(0,9) for _ in range(1000000)])
0.13007402420043945
[8, 1, 3, 0, 4, 5, 9, 2, 6, 7]
>>> testList([random.randint(0,9) for _ in range(1000000)])
0.1324453353881836
[0, 4, 5, 9, 7, 3, 6, 2, 8, 1]

# listのset
>>> testSet1([random.randint(0,9) for _ in range(1000000)])
0.015883684158325195
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> testSet1([random.randint(0,9) for _ in range(1000000)])
0.015897035598754883
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> testSet1([random.randint(0,9) for _ in range(1000000)])
0.015883684158325195
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

# listを回してsetにadd
>>> testSet2([random.randint(0,9) for _ in range(1000000)])
0.09662246704101562
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> testSet2([random.randint(0,9) for _ in range(1000000)])
0.09584259986877441
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> testSet2([random.randint(0,9) for _ in range(1000000)])
0.09663128852844238
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

ただ要素の種類を取り出したいだけならset使おう。