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演算子がほしい。