and
、or
で書く場合
Pythonのif
文で複数の条件を指定するときは、and
やor
を使うことが多いと思います。
if a > 0 and a % 2 == 0 and a != 10 and a != 100:
pass
ただ、この書き方では条件が増えると1行が長くなりすぎてしまいます。
しかも、条件の区切りが若干わかりづらい気もします。
そこで改行を入れることもできます。
if (
a > 0 and
a % 2 == 0 and
a != 10 and
a != 100
):
pass
こう書くこともできます。
if (
a > 0
and a % 2 == 0
and a != 10
and a != 100
):
pass
1行が短くなって見やすくなりました。
しかし、and
がある行とない行があるのが少し気になります。
if (
a > 0 and
a % 2 == 0 and
a != 10 and
a != 100 # この行だけandがないのが気になる
):
pass
そこでand
とor
の代わりにall
とany
が使えるんじゃないかと思いました。
all
、any
で書く場合
all
はPythonの組み込み関数で、リストなどの中身がすべてTrue
であればTrue
を返す関数です。
また、any
は中身に一つでもTrue
があればTrue
を返します。
これを使うと上のコードは下のように書き換えることができます。
if all([
a > 0,
a % 2 == 0,
a != 10,
a != 100,
]):
pass
おお、見やすい!
すべての条件式が揃っているので美しい気がします。
in
、not in
を使って書く場合
さらにin
やnot in
を使うことを思いつきました。
if False not in [
a > 0,
a % 2 == 0,
a != 10,
a != 100,
]:
pass
見た目的にはall
を使う場合とあまり変わりません。
しかし、「False
が入っていない」ということがand
と同じということはちょっと直感的ではない気がします。
速度の比較
実行速度はどうでしょうか。比較してみました。
$ python -m timeit 'True and False and False and True'
10000000 loops, best of 5: 22.7 nsec per loop
$ python -m timeit 'all([True, False, False, True])'
2000000 loops, best of 5: 102 nsec per loop
$ python -m timeit 'False not in [True, False, False, True]'
10000000 loops, best of 5: 39.6 nsec per loop
$ python -m timeit 'False not in {True, False, False, True}'
10000000 loops, best of 5: 34.4 nsec per loop
こうしてみると、やはりand
を使うのが一番速いようです。
not in
を使う方法は意外と健闘しています。リストの代わりに集合を使うことで若干高速化されるようです。
この中では一番遅いとはいえ、all
を使う方法も致命的に遅いわけではないので、実用的だと思います。
というわけで、条件式が増えた場合、and
の代わりにall
、or
の代わりにany
を使うのは良さそうだと思いました。
短絡評価に注意
and
を使った場合、一番左の条件がFalse
なら、右の条件は評価されずにFalse
であると評価されます。
一方でall
を使った場合、一旦リストを生成するため、すべての条件が評価されます。
単に右側の条件も評価するから遅い、というだけならあまり問題はないのですが、次のようなケースで問題が起こります。
if a is not None and a > 0: # aはNoneかもしれないが、短絡評価によってエラーが出ない
pass
これを
if all([
a is not None, # aはNoneかもしれない
a > 0, # aがNoneのときも評価され、エラーが発生する
]):
pass
こう書き換えてしまうと、コメントに書いたようにエラーが起きてしまいます。
これはどうすればいいんでしょうか。
None
判定だけ出すのは少し変な気がします。
if all([
a is not None,
b is not None,
]) and all([
a > 100,
b > 1000,
]):
pass
短絡評価されるall
演算子がほしい。