Pythonで~Trueと書くと結果は何になるでしょうか。正解は-2です。Trueではありません。この直感に反する挙動が問題視されてきました。実際、Python 3.12からbool型のビット反転演算子(~)は非推奨になっています。そこで今回は、Python 3.16で予定されている~True廃止の背景と移行対応について解説します。
Pythonの~True廃止議論の背景
Pythonのboolはintのサブクラスです。つまり、TrueはFalseの反対であると同時に整数の1でもあります。しかし、この設計が~演算子で混乱を生んでいました。
具体的には、~Trueの結果は-2になります。なぜなら、ビット反転は整数としての演算だからです。1のビット反転は-2です。さらに、bool(-2)はTrueに評価されます。つまり、~TrueをTrueの否定として使おうとすると予想外の結果になるのです。
また、~Falseの結果は-1です。bool(-1)もTrueです。したがって、~FalseもTrueになります。これは論理否定として全く機能していません。実際、多くの開発者がこの挙動を誤解して使っていました。特に、NumPyやpandasの経験があるとブール配列で~を使うため、通常のboolでも同じ感覚で使いがちです。
Python 3.16での変更内容と影響
Python 3.12から非推奨の警告が出るようになりました。そして、3.16では完全に削除される予定です。つまり、~Trueや~Falseを書くとエラーになります。
しかし、影響範囲は限定的です。なぜなら、ほとんどの場合はnot演算子を使うべきだったからです。具体的には、not TrueはFalseを返します。また、not FalseはTrueを返します。これが期待される論理否定の動作です。そのため、~をnotに置き換えるだけで対応できます。
ただし、一部のケースでは注意が必要です。たとえば、ビットマスク操作で意図的にboolのビット反転を使っていた場合です。しかし、そのような使い方は極めて稀です。むしろ、int型にキャストしてからビット反転する方が意図が明確です。さらに、リテラルに対する~の非推奨警告が出ないバグも報告されていました。したがって、コードベース全体の確認が推奨されます。
~True廃止への移行対応の実践
移行作業は比較的簡単です。そこで、具体的な手順を紹介します。
まず、コードベースで~を検索します。特に、boolに対して~を使っている箇所を特定しましょう。具体的には、grepやIDEの検索機能でパターンを洗い出します。また、変数名がboolかどうか型アノテーションを確認しましょう。
次に、notへの置き換えを行います。たとえば、~is_validをnot is_validに変えます。さらに、テストを実行して動作が変わらないことを確認します。しかし、pandasやNumPyの配列に対する~はそのまま使えます。なぜなら、これらのライブラリは独自の__invert__メソッドを実装しているからです。したがって、配列操作の~は廃止の対象外です。
加えて、CIパイプラインにPython 3.12以降の警告チェックを追加するのも効果的です。具体的には、-Wdフラグを使ってDeprecationWarningを表示させます。つまり、テスト実行時に自動的に問題箇所が検出されるようにするのです。このように、自動化しておくと見落としを防げます。
boolとintの関係が生む他の落とし穴
~以外にもboolとintの関係で注意すべき点があります。まず、True + Trueは2になります。つまり、boolの足し算は整数の足し算として扱われるのです。
また、True * 5は5になります。しかし、これは意図的に使われることもあります。たとえば、条件に応じた乗算に便利です。さらに、リスト操作でも影響があります。[0] * Trueは[0]を返します。特に、Falseを掛けると空リストになります。
とはいえ、これらの挙動はPEP 285で意図的に設計されたものです。なお、Python開発チームはboolとintの継承関係を廃止する議論はしていません。だからこそ、~の廃止は「特に問題のある挙動」にのみ対処した形です。実際、~の廃止は最も混乱を招きやすい操作を取り除くという実用的な判断です。
Pythonの~True廃止のまとめ
~Trueの廃止は小さな変更に見えますが重要な改善です。しかし、移行作業自体はnotへの置き換えで済む場合がほとんどです。だからこそ、Python 3.16がリリースされる前に対応を済ませておきましょう。特に、大規模なコードベースでは早めの検索と修正が安心です。まずは自分のプロジェクトで~がboolに使われていないか確認してみてください。
