イテレータのコピー


イテレータはコピーできない。

>>> import copy
>>> def it():
...     for i in xrange(10):
...         yield i
...
>>> list(it())
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a = it()
>>> b = copy.copy(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python26\lib\copy.py", line 95, in copy
    return _reconstruct(x, rv, 0)
  File "C:\Python26\lib\copy.py", line 323, in _reconstruct
    y = callable(*args)
  File "C:\Python26\lib\copy_reg.py", line 93, in __newobj__
    return cls.__new__(cls, *args)
TypeError: object.__new__(generator) is not safe, use generator.__new__()


代わりにitertools.teeを使う。

>>> import itertools
>>> a = it()
>>> b = itertools.tee(a)
>>> a, b = itertools.tee(a)
>>> list(a)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(a)
[]
>>> list(b)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


ちなみに、xrangeはコピーできる。

xrangeはイテレータではないから。

>>> a = xrange(10)
>>> b = copy.copy(a)
>>> a
xrange(10)
>>> b
xrange(10)
>>> a.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'xrange' object has no attribute 'next'


xrangeをfor文を使わずイテレータとして利用するなら、

iter関数でイテレータに変換すればいい。

しかし、当然コピーは出来なくなる。

>>> a = xrange(10)
>>> iter(a)
<rangeiterator object at 0x009EC530>
>>> ait = iter(a)
>>> ait.next()
0
>>> copy.copy(ait)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python26\lib\copy.py", line 95, in copy
    return _reconstruct(x, rv, 0)
  File "C:\Python26\lib\copy.py", line 323, in _reconstruct
    y = callable(*args)
  File "C:\Python26\lib\copy_reg.py", line 93, in __newobj__
    return cls.__new__(cls, *args)
TypeError: object.__new__(rangeiterator) is not safe, use rangeiterator.__new__()