消えた「細かすぎて伝わりにくい、Pythonの本当の落とし穴10選」

かつて存在した「[python] 細かすぎて伝わりにくい、Pythonの本当の落とし穴10選」という記事が、元サイトから消えてしまっている。

* なくなった当該記事: [[python] 細かすぎて伝わりにくい、Pythonの本当の落とし穴10選](http://kwatch.houkagoteatime.net/blog/2014/08/24/python-pitfalls/)
  * Web上のアーカイブ: https://archive.is/jKNp1
* 参照している例: [Pythonを書き始める前に見るべきTips](http://qiita.com/icoxfog417/items/e8f97a6acad07903b5b0)
  * (2017-06-16) いつのまにかこの記事をリンクするようになっているようだ！！

ググっても存在しないのも流石に背中が痒い感じがする。私自身の個人的な情報保全のため、その10項目をここに挙げておく。

* 自作の test.py を import しようとしてもできない
* スクリプトファイルを更新したのに、実行しても動作が変わらない
* exception Err1, Err2: と書くと、Err2 を取りこぼす (Python2)
* 「,」があるだけでタプルになってしまう
* 連続した文字列リテラルは自動的に連結されてしまう
* 文字列の % 演算子が、タプルとそれ以外で挙動が変わる
* bool型が、実はintのサブタイプである
* intとlongに共通する親クラスがない (Python2)
* デコレータに複雑な式を書くと文法エラー
* 同じコードでもPythonのバージョンによって挙動が変わる

オリジナルの日付は「2014/08/24」で、特にPython2のルールに内容が引きづられていることに注意しよう。上記のWebアーカイブが本文をそのまま残しているので、原典を当たりたければアーカイブにて内容を見て欲しい。

以下、本文書筆者としても気になったものだけ抽出してメモを書く

# 自作の test.py を import しようとしてもできない

標準ライブラリに`test`モジュールあるんだよ！流石に罠っぽい気がするよね！

https://docs.python.org/ja/3/library/test.html

実はこのエントリの最初はこれを取り上げてなかったんだけど、時を経て格上げ。


# bool型はint型のサブタイプ

Trueを1、Falseを0で代用していた頃の名残、ということのようだ。関数冒頭で動的に型をチェックするなどするときにハマるケースがあるかもしれない。

```py
Python 3.5.2 (default, Jul 29 2016, 11:13:25)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> issubclass(bool, int)
True
>>> True == 1
True
>>> False == 0
True
>>> True > 0
True
>>> True < 10
True
>>> True < 1
False
```

Python2ではTrue/Falseはグローバル変数ということで、上書きまで出来る。

```py
Python 2.7.12 (default, Jul 20 2016, 12:19:03)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 0
>>> False = 1
>>> print 'true' if True else 'false'
false
```

結構驚きだが、定数という概念がない以上仕方ない。本エントリの筆者も、大昔のソースコードでわざわざ `True = 1` と `False = 0` を条件分岐して定義しているのを見たことがある。

Python 3ではTrueとFalseは予約語となっており上記の問題は起きない。

```py
Python 3.5.2 (default, Jul 29 2016, 11:13:25)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 0
  File "<stdin>", line 1
SyntaxError: can't assign to keyword
```

# 文字列の % 演算子が、タプルとそれ以外で挙動が変わる

```py
Python 3.5.2 (default, Jul 29 2016, 11:13:25)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> "%r" % (1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting
>>> "%r" % ((1, 2),)
'(1, 2)'
```

本文筆者は普段 `str.format()` を優先して選択するのであまり気にならないが、ハマると言えばハマるかもしれない。

スモークテストで軽く落ちる挙動をそこまで心配する必要はない気がするのだけれども、十分なカバレッジのないコードの例外パスでデバッグログに対してこういう情報を仕込んで、例外時に無用な例外がさらに出て困るようなケースは想像出来る。

追記（2021-09-18）: f-string まで導入された今、理由もなしに `%` で文字をフォーマットする人は大分減ったとは思う、かな……

# 感想

記事がリンク切れだと「落とし穴10個！？怖っ」なんて無闇に心配になるのだが、実際に見てみると、多くはそこまで気にするほどの落とし穴ではないと思う。良くも悪くも少し時代(?)を感じる記事だ。味わおう。

[『Python文法詳解』](https://www.amazon.co.jp/dp/4873116880/)などを読めば、わざわざPython 2に戻らなくともPython 3に関わる文法で1つや2つ、知らないものは見つかると思う。
同書の発行日は2014/09/18で上記記事の日付に近い (執筆準備を考えればもっと前の文書のはずだ)。
しかし、内容は Python 3.3を前提としておりPython 2の影を見ることがないという意味で好対照を成すなかなかの良著だ。
最近のPython本ブームの影に隠れて目に留まりづらいが、興味があれば覗いてみると良いと思う。

# Extra Stage! (2017-06-16)

せっかくなので、本編とは別に定番の落とし穴と回避策を書いとく。

```
>>> 0.1 + 0.1 + 0.1 == 0.3
False
>>> import math
>>> math.isclose(0.1 + 0.1 + 0.1, 0.3)
True
```

浮動小数点の比較、とくに equality には注意が必要だ。
Python 3.5から mathモジュールに isclose() という関数が入ったので、気になる折には使用を検討しよう。

参考: https://docs.python.jp/3.5/library/math.html#math.isclose

# 追記 (2021-09-18)

まだLGTMボタンとか押してくれる人がいるんだ！すごい！

2014年は遠い昔になったし、Python 2はもう流石に過ぎ去ったこともあるので、本文のトーンを調整。気をつける場所は減ったけど変わらず面倒な `test` モジュール……