[Python]andと&の挙動の違い

はじめに

今回、1年ぶりにPythonでbool演算子を書いた。

ここ最近は、仕事でもGo言語ばかり触っていたので、andではなく&&と書いてしまった。

そしたら、テキストエディタ様に怒られ、1つ消して&としたところでテキストエディタが許してくた。

『あ、こうだっけー?』なんて思いながら、動作確認してみたらbool演算してくれてそうだったら、そのままコーディングを進めてしまい、発見が遅れてしまってハマったので、残しておこうと思う。

いきなり全コード

flag_a = True
flag_b = True

# 1. 論理AND演算子での比較
if flag_a and flag_b:
    print("1. True!")
else:
    print("1. False...")
    
# 2. 誤って&で比較しようとした場合
if flag_a & flag_b:
    print("2. True!")
else:
    print("2. False...")
    
# Trueではなく謎の数値2が入っていた場合
flag_a = 2
    
# 3. 論理AND演算子での比較し、flag_aに謎の数値2が入っていた場合
if flag_a and flag_b:
    print("3. True!")
else:
    print("3. False...")
    
# 4. 誤って&で比較し、flag_aに謎の数値2が入っていた場合
if flag_a & flag_b:
    print("4. True!")
else:
    print("4. False...")

# 1. True!
# 2. True!
# 3. True!
# 4. False...

そもそも

andってなんだっけ

後で考えたら当然なんだけど、Pythonではbool演算子ではandを使う。

flag_a = True
flag_b = True

# 1. 論理AND演算子での比較
if flag_a and flag_b:
    print("1. True!")
else:
    print("1. False...")

# -> 1. flag_a and flag_b is True!

プログラミングの初歩の部分なので、細かい説明は省略するけど、こんな感じで動く。

&ってなんだっけ

&はビット論理AND演算子と呼ばれる。

一般的な挙動としてはこんな感じ。

print(10 & 12)

# -> 8

これだけ見るとワケ分からないかもしれないけど、めっちゃ要約すると&はビット…つまり2進数にした状態で演算するものらしい。

つまり、10進数の10を2進数にした1010と、10進数の12を2進数にした1100を使って演算するということになる。

ビット論理AND演算子は、各値の対応する桁を見比べて、どちらの値も1だった場合のみ1になる。

(それ以外の場合は全て0になる)

↓こんな感じ

1010  => 10
1100  => 12
------------
1000  => 8

これによって1000という結果が導き出され、これを10進数に直すと8になる。

bool演算のつもりで&を使うと

こんな風に、まったく違うand&だけど、bool演算子と間違えて使うと、発見が遅れることがある。

挙動が似ている

本来なら↓こう書くべきところを…

flag_a = True
flag_b = True

# 1. 論理AND演算子での比較
if flag_a and flag_b:
    print("1. True!")
else:
    print("1. False...")

# -> 1. True!

間違えて↓こう書いてしまったら。

flag_a = True
flag_b = True

# 2. 誤って&で比較しようとした場合
if flag_a & flag_b:
    print("2. True!")
else:
    print("2. False...")

# -> 2. True!

出力結果は同じになる。

これは、Trueを2進数で表現すると1になるが、TrueTrueだと当然11の演算のため、1が導き出され、Trueになるためだ。

しかし当然、挙動が変わることもある

このせいで、ちゃんとbool演算できてそうだなーなんて思ってたら、なんか挙動が違うことも…

これのせいでめっちゃハマった。

flag_a = 2  # Trueではなく謎の2という数値が入っていたら
flag_b = True
    
# 3. 論理AND演算子での比較し、flag_aに謎の数値2が入っていた場合
if flag_a and flag_b:
    print("3. True!")
else:
    print("3. False...")
    
# 4. 誤って&で比較し、flag_aに謎の数値2が入っていた場合
if flag_a & flag_b:
    print("4. True!")
else:
    print("4. False...")

# 結果
# 3. True!
# 4. False...

bool演算子とビット論理AND演算子で違う値が出る。

これは先程の説明通り、Trueは2進数にすると1になるが、10進数の2は2進数にすると10になる。

これをビット論理AND演算子で計算すると、↓の通りFalseになってしまう。

01  => True
10  => 2(10進数)
------------
00  => False

そもそも、2Trueをbool演算してTrueって出すのもどうかと思うけど、Pythonはどこまでいっても動的型付け言語だし、こんなバグって残念ながら結構ある。

最後に

今回は、bool演算子のつもりで&を使ってしまい発見が遅れたことを紹介しつつ、and&の挙動の違いついて紹介しました。

一見想定通り動いてるコードって、発見が遅れますよね。

最後まで読んで頂いてありがとうございます。

参考サイト

ビット演算に関する演算子
Python で用意されている数値型の値で利用可能な演算子の中で、ビット演算に関する演算子に関して解説します。

PON3

PON3

5年くらい前に、30代完全未経験から独学でWeb系のソフトウェアエンジニアになった人。 現在はフリーランスエンジニアとして、大阪の自宅からフルリモートで東京の自社開発企業のお仕事をしている。 主戦場はバックエンドで、Go言語でのAPI開発や、Pythonでのデータ分析が武器。 とは言いつつ、フロントエンドをSPAを実装したり、IaCでクラウドインフラの設計構築したり、スクラム開発でプロジェクト運営したりするなんでも屋。 いつも、ググってきては誰かが書いてくれた記事を見て開発していたが、もらってばかりでなく世の中に返すこともしたいと思い、技術ブログをはじめる。 妻と2歳になる娘の3人暮らし。 何かご用件がある方は、TwitterのDMからどうぞ。