概要
たまーにしか使わないけど、忘れるのでメモメモ。
組み込み関数 iter()
には、引数が一つ版と引数2つ版があります。
それぞれ、書式は以下となります。
iter(iterable)
iter(callable, sentinel)
なんか、二つ目の引数がちょっと変・・・w
iter()
さんは、引数が二つの場合は callable
を受け取ります。
で、sentinel
の値が来るまで、ずっと callable
を呼び続けるという動作をします。
sentinel
ってのは、日本語でいうと「番兵」です。プログラミングでよく出てきますね。
なので、何かの値が現れるまで、ずっと処理続けるってときに
results = set() for x in y: if x == sentinel: break results.add(x)
って書くところを
for x in iter(callable, sentinel): results.add(x)
または
results = {x for x in iter(callable, sentinel)}
って書けてちょっとスマートな感じになります。
で、よくやってしまう失敗として list
を渡してしまう。
l = list(range(10)) r = [x for x in iter(l, 5)]
実行すると
TypeError: iter(v, w): v must be callable
ってなります。list は callable では無いからです。
で、よく使われるのがやっぱりファイル処理とか、バイトデータを検索したりするときとか、ランダムなデータが来るので特定の値が来るまで続けるとか。
何らかの値が来たら終わりってするときに
with open('xxxxx', mode='r', encoding='utf-8') as f: for line in iter(f.readline, 'hoge'): print(line)
ってするといい感じ。(PythonのAPIリファレンスの方は、空文字で見てますが)
でも、問題点は sentinel の値が固定で判定されているという点です。
実際につくる時に、ここが固定じゃない場合があります。
何らかの値の内のどれかが来たら終わりにするとか。
その場合は、sentinel を表現するものを作ってやるとうまくいきます。
内部では、== で一致するかを見ているので、__eq__(self, other)
を定義してやればいいはず。
(お作法として、__ne__(self, other)
も定義しておくほうが良いですね。)
サンプル
以下、とりあえず忘れないためのクソサンプル。
# coding: utf-8 """ 組み込み関数 iter(callable, sentinel) についてのサンプルです。 """ import os from trypython.common.commoncls import SampleBase from trypython.common.commonfunc import pr class MySentinel: def __eq__(self, o: object) -> bool: return 'sentinel' == str(o).replace('\n', '') class Sample(SampleBase): def __init__(self) -> None: super().__init__() self._file = '/tmp/test.txt' def exec(self): #################################################### # iter(callable, sentinel) # # 組み込み関数の iter は、引数が一つの場合と # 二つの場合で挙動が異なる。 # # 引数が一つの場合、要求される引数は iterable だが # 引数が二つの場合、要求される引数は # callable と sentinel # となる。 # # sentinel は、日本語で言うと「番兵」の事。 # iter(callable, sentinel) は、毎回 callable を # 呼び出して、その値が sentinel と等しいかを判定する。 # # 等しい場合は、StopIteration が発生して # イテレーションが終わる。 # # なので、何かの値が出てくるまで継続するという # 処理と記述する時に使える。 #################################################### try: self._write_dummy_file() with open(self._file, mode='r', encoding='utf-8') as f: sentinel = MySentinel() it = iter(f.readline, sentinel) for i, line in enumerate(it): pr(f'line-{i:02}', line) finally: self._delete_dummy_file() def _write_dummy_file(self): with open(self._file, mode='w', encoding='utf-8', newline='') as f: print('helloworld', file=f) print('sentinel', file=f) print('helloworld', file=f) def _delete_dummy_file(self): if os.path.exists(self._file): os.remove(self._file) def go(): obj = Sample() obj.exec() if __name__ == '__main__': go()
実行すると以下となります。
line-00='helloworld\n'
参考情報
2. 組み込み関数 — Python 3.6.3 ドキュメント
3. データモデル — Python 3.6.3 ドキュメント
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場