python setの和集合・積集合の計算に、and/orは使えない

タイトルの通りである。 python setの和集合・積集合の計算にand/orは使えない。

ミスってからすぐ気づいたので大きな手戻りにはならなかったが、備忘録として今後忘れないように書き留めておく。

setに対してand/orを使うと何が起きるか

Pythonのバージョン:Python 3.7.4

PowerShellpythonと打ってインタプリタ形式で起動した。

>>> x = {1, 2, 3}
>>> y = {2, 3, 4}
>>> x and y
{2, 3, 4}  # 集合のand、積集合ならば{2, 3}になるはず
>>> x or y
{1, 2, 3}  # 集合のor、和集合ならば{1, 2, 3, 4}になるはず

上記の例では、意図したとおりに動いていないのが明らかである。 しかし、実際に作業をしていたときは、両方の集合が色々と演算した結果だったので、返ってきた集合が意図通りでないということがすぐには分からず、その後コードを書き進めて「あれ?何かおかしくないか?」となった。 幸い、「python set 積集合」で検索してすぐに正解にたどり着いた。

正しい書き方

和集合には | 、積集合には& を使う。

>>> x = {1, 2, 3}
>>> y = {2, 3, 4}
>>> x & y  # 集合のand、積集合
{2, 3}
>>> x | y  # 集合のor、和集合
{1, 2, 3, 4}

他にも、差集合、対称差集合の演算子もある。詳しくは公式ドキュメントを参照のこと。
組み込み型 — Python 3.8.0 ドキュメント

仕組み

では、何でand/orを使ったら上記のような挙動になったのか?この理由は、以下の2点によって説明できる。

  • x and yの仕様は「x が偽ならx, xが真ならばyを返す」である
  • set型(集合)のオブジェクトの真偽値判定は、空集合でないならTrue、空集合ならFalseを返す

1点目の公式ドキュメントはここ。
組み込み型 — Python 3.8.0 ドキュメント
PyQのブログによる、もう少し詳しい解説はここ。
Pythonのandとorはif文以外でも使える?andとorの動作が面白いという話をします - Python学習チャンネル by PyQ

2点目の公式ドキュメントはここ。
組み込み型 — Python 3.8.0 ドキュメント
ここを見ると、bool()を使えば真偽値を判定した結果(ブール値にキャストした結果)が返ってくるのね。
組み込み関数 — Python 3.8.0 ドキュメント
int()はよく使うけど、bool()は違和感があるな……

やってみよう。

>>> bool({1, 2, 3})
True
>>> bool(set())
False

1つ以上の要素の入ったsetはTrue、空のsetはFalseになっている。 ({}と書くと、空のsetではなく空の辞書になってしまう。空のsetはset()と書く。)

以上の2点を踏まえて、もう一度最初の例に戻ろう。

>>> x = {1, 2, 3}
>>> y = {2, 3, 4}
>>> x and y 
{2, 3, 4}  # 最初にxの真偽値を評価する。xは空でないsetなのでTrueである。したがってyを返す。
>>> x or y
{1, 2, 3}  # 最初にxの真偽値を評価する。xは空でないsetなのでTrueである。したがってxを返す。

文法規則に従っているから、言われてみればその通りだけど、初見だとビックリするなぁ。 「おいおい、そこで使ってるand/orは和集合・積集合の演算子じゃない、論理演算子だぞ。大丈夫か?」という警告をしてくれるわけでもない。仕様をちゃんと理解して気を付けよう……