Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

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

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

オリジナルの日付は「2014/08/24」となっておりそこまで古くないが、
その割に古めのPython (特にPython 2) のルールに内容が引きづられすぎている気はする。
古くからPython 2を使い続けてきた方なのだろう。

上記のアーカイブが本文をそのまま残しているので、原典を当たりたければアーカイブにて内容を見て欲しい。
以下、本文書筆者としても気になったものだけ抽出してメモを書く

bool型はint型のサブタイプ

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

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はグローバル変数ということで、上書きまで出来る。

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

結構驚きだが、定数という概念がない以上仕方ない。

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

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

exception Err1, Err2: と書くと、Err2 を取りこぼす (Python2)

ほぼ、オリジナルのコードの全文引用になるが、以下に実例を示す。

from datetime import date
try:
  date(2014, 2, 30)
except (TypeError, ValueError):  # 複数の例外クラスを一度に指定。正しい
  print("error")
from datetime import date
try:
  date(2014, 2, 30)
except TypeError, ValueError: # except TypeError as ValueError と同じ意味になり、ValueErrorクラスをexceptが取りこぼす
  print("error")

Python 2 では後者は例外が「漏れて」しまう。
ちょっとしたケアレスミスで起きる事象としては少しキツい。

Python 3 では後者が文法エラーになり as 一択になるので問題にならない。

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

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() を優先して選択するのであまり気にならないが、ハマると言えばハマるかもしれない。

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

感想

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

『Python文法詳解』などを読めば、わざわざ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

amedama
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away