4.6. andor の奇妙な性質

Python では, andor は期待通りに論理演算を行いますが, 論理値を返すわけではありません. その代わりに, 論理演算を行っている実際の値のうちの 1 つを返します.

Example 4.15. and の紹介

>>> 'a' and 'b'         1
'b'
>>> '' and 'b'          2
''
>>> 'a' and 'b' and 'c' 3
'c'
1 and を使うと, 左から右の順で論理値として値が評価されます. 0, '', [], (), {}, None は論理値としては偽で, 残りは全て真です. ただ, ほぼ全てです. デフォルトではクラスのインスタンスは論理値としては真ですが, 自作のクラスに特別なメソッドを作ることで, インスタンスを偽と評価させることができます. 5 章 でクラスや特別なメソッドについて全て学びます. 全ての値が論理値として真だった場合, and は最後の値を返します. この例では, and'a' を真, 'b' も真と評価し, 'b' を返します.
2 論理値として偽の値がある場合には, and は最初の偽の値を返します. この例では, '' が最初の偽の値です.
3 全ての値は真なので, and は最後の値 'c' を返します.

Example 4.16. or の紹介

>>> 'a' or 'b'          1
'a'
>>> '' or 'b'           2
'b'
>>> '' or [] or {}      3
{}
>>> def sidefx():
...     print "in sidefx()"
...     return 1
>>> 'a' or sidefx()     4
'a'
1 or を使うと, and と同じように左から右の順で論理値として値が評価されます. 真の値がある場合には, or はすぐにその値を返します. この例では, 'a' が最初の真の値です.
2 or'' を偽, 'b' を真と評価し, 'b' を返します.
3 全ての値が偽だった場合には, or は最後の値を返します. or'' を偽, [] も偽, {} も偽と評価し, {} を返します.
4 or は論理値として真の値を見付けるところまでしか値の評価を行わず, それ以降は無視することに注意してください. ある値に副作用がある場合に, この区別は重要です. ここでは, or'a' を真と評価するので, 関数 sidefx は決して呼び出されず, 'a' がすぐに返ります.

あなたが C ハッカーなら, bool ? a : b という式 (bool が真なら a を, そうでなければ b を評価する) に馴染んでいるはずです. Pythonandor の振舞いのおかげで, 同じことができます.

4.6.1. and-or トリックを使う

Example 4.17. and-or トリックの紹介

>>> a = "first"
>>> b = "second"
>>> 1 and a or b 1
'first'
>>> 0 and a or b 2
'second'
1 この構文は Cbool ? a : b 式に似ています. 式全体は左から右へ評価されていくので, and が最初に評価されます. 1 and 'first''first' と評価され, さらに 'first' or 'second''first' と評価されます.
2 0 and 'first'0 (訳注. 原文では False となっているがおそらく間違い) と評価され, さらに 0 or 'second''second' と評価されます.

しかし, この Python の式は単に論理式で, 特別な言語の構造ではないので, この Pythonand-or トリックと Cbool ? a : b 構文には決定的な違いがあります. a の値が偽の場合, 式はあなたが期待した通りには動きません. (私がこれにやられたことがあるのが分かるかい? 1 回より多いと思う?)

Example 4.18. and-or トリックが失敗するとき

>>> a = ""
>>> b = "second"
>>> 1 and a or b         1
'second'
1 a が空文字列なので, Python の論理値としては偽として扱われ, 1 and '''' と評価され, 次に '' or 'second''second' と評価されます. おぉっと! これは欲しいものとは違います.

and-or トリック (bool and a or b) は, a が論理値として偽のときには Cbool ? a : b 式のようには動きません.

and-or トリックの裏に仕掛ける本物のトリックは a の値が絶対に偽にならないようにすることです. 1 つのよくあるやり方は a[a] に, b[b] に変え, 返り値のリストの最初の要素 (a または b) を取ることです.

Example 4.19. 安全な and-or トリックを使う

>>> a = ""
>>> b = "second"
>>> (1 and [a] or [b])[0] 1
''
1 [a] は空リストではないので偽にはなりません. a0'' や他の偽の値であっても, リスト [a] は要素を 1 つ持つので真です.

ここまでで, このトリックは役に立つよりも問題の方が多そうに見えます. そもそも, if 文を使って同じ結果を得られるのに, 何故こんなどうでも良さそうなことを扱ったのでしょうか? まぁ多くの場合, あなたは 2 つの定数のうちどちらかを選ぼうとしているので, 単純な方の構文で十分で心配は要りません. なぜなら, a の値は常に真だと把握しているからです. そして, 複雑で安全な形を使う必要が出てきたとしても, そうする十分な理由があります. 例えば, lambda 関数の中など, Python には if 文が書けない場面があります.

(訳注. Python 2.5 から a if bool else b という構文がサポートされたので, その環境で開発する限りここで書かれているトリック無しでも C の三項演算子と同等のことが実現できます.)

and-or トリックについてさらに知るには