tee関数は同じイテレータを複数回、使いたいときに便利な関数です。イテレータは一度使うと全く新しく生成しなおすしかありません。しかしteeを用いることで生成は1回でイテレータを全く別々に複数回使うことができます。
複数回同時に使うといっても実体は最初のイテレータなので毎回生成するよりメモリや処理の点で有利になります。
tee関数は同じイテレータを複数回、使いたいときに便利な関数です。イテレータは一度使うと全く新しく生成しなおすしかありません。しかしteeを用いることで生成は1回でイテレータを全く別々に複数回使うことができます。
複数回同時に使うといっても実体は最初のイテレータなので毎回生成するよりメモリや処理の点で有利になります。
1つのイテラブルから複数のイテレータを生成する関数です。第1引数にイテラブル、第2引数にいくつイテレータを生成するかを指定します。
なぜtee関数が必要かというと、きちんとした理由があります。イテラブルをn回使いたいのであればn個生成してループをn回まわせば済みます。しかしそれだとn個分のリソースが必要になります。それでは共通化すれば良いということになりますが、イテレータは一度値を取り出したら再度取り出すことができません(リストやタプルなどのイテラブルの一部は値をメモリ上に保持しているため何度でも取り出すかとができますがメモリ領域を使用します)。もしもう一度使いたいのであれば結局、イテレータを再度生成することになります。
一方tee関数では1つのイテラブルを複数のイテレータにわけることができます。tee関数で生成されたイテレータでは生成した値を効率的に使い回すことでリソースを無駄にせずに効率的にイテレータが生成されるように工夫されています。
実際にtee関数を用いた実装サンプルをみてみます。
it=iter(range(0,3))
it0,it1,it2=tee(it ,3)
print(it0)
print(it1)
print(it2)
print([i for i in it0])
print([i for i in it1])
print([i for i in it2])
itイテレータがteeによりit0~it2の3つのイテレータに分岐しています。その下のprintではit0~it2がイテレータになっているか確認しています。
さらにその下の内包表記ではit0~it2のイテレータの中からじっさいに値を取り出しています。実行結果は以下のようになります。
実行結果
<itertools._tee object at 0x000001C52A746840> <itertools._tee object at 0x000001C52B088D80> <itertools._tee object at 0x000001C52B088A00> [0, 1, 2] [0, 1, 2] [0, 1, 2]
このようにteeで指定した分岐数分だけイテレータとして用いることができます。
ちなみにこの処理の後で元となったitのイテレータがどうなっているかというと空になっています。
print([i for i in it])
実行結果
[]
では逆にit0~it2のイテレータから値を取り出す前にitから値を取り出すとでうなるでしょうか。
from itertools import tee
it=iter(range(0,3))
it0,it1,it2=tee(it ,3)
print(it)
print(it0)
print(it1)
print(it2)
print([i for i in it])
print([i for i in it0])
print([i for i in it1])
print([i for i in it2])
実行結果
<range_iterator object at 0x000001E9BAFD1A70> <itertools._tee object at 0x000001E9BB9D8E00> <itertools._tee object at 0x000001E9BB9D8AC0> <itertools._tee object at 0x000001E9BB9D8A00> [0, 1, 2] [] [] []
もとのイテレータが回りきってしまうとコピーしたイテラブルも同時に空になります。元のイテレータを回すと分岐したイテレータに影響があるため元のイテレータは触らないように注意してください。
この記事ではtee関数の説明をおこないました。また使用例をとおして、使い方をみてみました。
Comment on this article
コメントはまだありません。
Send comments