【Python】実際に見たちょっとあれなコード達と改善案
# python
自戒も込めて書き記していきます。そして世の中にいいコードがたくさん生まれますように。
実際の製品の中でも速度に大きく問題があるようなコードが動いていることも良くあるので、やはり勉強は大切だし差別化にもなりますよね… 見つける度に追加していきます…
listじゃなくてsetで比較するのだ
setは被りなく順序なく、要素を格納してくれる機能です。(setの紹介はこちら)
なのに何故要素に順序がある前提で比較してしまうんだ…リストにしてしまうんだ…
import pandas as pd
series_1 = pd.Series([0,1,1,1,0,1])
is_binary = list(set(series_1)) == [0,1] or list(set(series_1)) == [1,0]
print(is_binary)
# --
series_2 = pd.Series([0,1,1,1,0,1,2])
is_binary = list(set(series_2)) == [0,1] or list(set(series_2)) == [1,0]
print(is_binary)
順序関係なく単純に中身を確認したい場合はset同士で比較しましょう。
series_1 = pd.Series([0,1,1,1,0,1])
is_binary = set(series_1) == {0,1}
print(is_binary)
# --
series_2 = pd.Series([0,1,1,1,0,1,2])
is_binary = set(series_2) == {0,1}
print(is_binary)
listから簡単な条件で要素を抽出する時は内包表記を使おう
速度的にも内包表記の方が有利なことが多いです。(参考)
list_a = []
for val in [1,2,3,4,5,6,7,8,9,10]:
if val % 2 == 0:
list_a.append(val)
print(list_a)
# [2, 4, 6, 8, 10]
ただ、それよりもPythonを書いている人の多くは内包表記を使ってリストを抽出することに慣れています。 そのためこの方が色々な人が見たときに「あー、これは○○なリストを作っているだけなんだな」というのが一瞬で分かります。
list_a = [val for val in [1,2,3,4,5,6,7,8,9,10]
if val % 2 == 0]
print(list_a)
# [2, 4, 6, 8, 10]
他の人に合わせる。大切。
なぜ辞書のキー判定にlistを使ったのだ!?
このような用途のためにリストを生成する必要はありません。
%%timeit
for index in df.index:
index in list(apple_dict.keys())
# 864 ms ± 13.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
- 存在有無の確認ならsetを使う
- ループの中で毎回生成せず、事前に生成しておく
- そもそもdict in keyでチェックできるのでそうする
%%timeit
for index in df.index:
index in apple_dict
# 1.24 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
実際にはループ内部での処理を何かしら行うので、ここまでは変化はありませんが、単純にループ部分だけで比較すると、約1/800の実行時間になります。 変わりすぎ…。