はじめに
今日コードを書いていて、rangeでもinが使えることに気づきました。
>>> 10 in range(20) True
ドキュメントを見るとシーケンス型としての機能は一通り備えているようです。
range オブジェクトは collections.abc.Sequence ABC を実装し、包含判定、要素インデックス検索、スライシングのような機能を提供し、負のインデックスをサポートします (シーケンス型 — list, tuple, range を参照):
ちなみにfloatでもなんとなくTrueになりましたが、あくまでも離散値の包含で比較される雰囲気です。
>>> 10.0 in range(20) True >>> 10.1 in range(20) False
測ってみる
こういうものがあると、速度が気になります。
>>> r = range(1000) >>> l = list(range(1000)) >>> s = set(l) >>> import timeit >>> timeit.timeit(lambda : 500 in r) 0.2108146829996258 >>> timeit.timeit(lambda : 500 in l) 8.18245309899794 >>> timeit.timeit(lambda : 500 in s) 0.13419233300010092 >>> timeit.timeit(lambda : 0 <= 500 < 1000) 0.12016064700219431
同じ長さのlistよりは圧倒的に速いものの、setに負けるという結果に。恐らくシーケンスに展開して線形探索をする訳ではないものの、内部処理がそこそこ複雑なのでしょう。また、単に値の区間だけ確認したいのなら、不等式による比較が最速のようです(setが異様に速いと言うべきか・・・)。
使いどころ
rangeということはstepも入れられるので、複雑な条件のときにはmodで書くより可読性が良いかもしれません。
>>> [x in r for x in range(10)] [True, False, False, True, False, False, True, False, False, True]
他に積極的に使う理由は思いつきません。