有没有办法在Python中复制任意生成器?

问题描述

函数式编程中最著名的功能是惰性求值和无限列表。在Python中,通常使用生成器来实现这些功能。但是函数式编程的戒律之一是不变性,而生成器也不是不变的。恰好相反。每当有人在生成器上调用next()时,它都会更改其内部状态。

一种可能的解决方法是在调用生成器之前next()复制一个生成器。这适用于某些生成器,例如count()。 (也许count()不是生成器?)

from itertools import count

count_gen = count()
count_gen_copy = copy(count_gen)
print(next(count_gen),next(count_gen),next(count_gen))  # => 0 1 2
print(next(count_gen_copy),next(count_gen_copy),next(count_gen_copy))  # => 0 1 2

但是,如果我定义了自己的生成器,例如my_count(),则无法复制它。

def my_count(n=0):
    while True:
        yield n
        n += 1


my_count_gen = my_count()
my_count_gen_copy = copy(my_count_gen)
print(next(my_count_gen),next(my_count_gen),next(my_count_gen))
print(next(my_count_gen_copy),next(my_count_gen_copy),next(my_count_gen_copy))

当我尝试执行copy(my_count_gen)TypeError: can't pickle generator objects时收到错误消息。

有没有解决的办法,或者还有其他办法?

也许另一种询问方式是:copy()在复制copy_gen时复制的内容是什么?

谢谢。

P.S。如果我使用__iter__()而不是copy(),则__iter__()的版本会像原始版本一样。

my_count_gen = my_count()
my_count_gen_i = my_count_gen.__iter__()
print(next(my_count_gen),next(my_count_gen))  # => 0 1 2
print(next(my_count_gen_i),next(my_count_gen_i),next(my_count_gen_i))  # => 3 4 5

解决方法

无法在Python中复制任意生成器。该操作只是没有意义。生成器可能依赖于其他各种不可复制的资源,例如文件句柄,数据库连接,锁,工作进程等。如果生成器持有并复制了锁,那么该锁会发生什么?如果生成器在数据库事务中,并且您将其复制,那么事务将如何处理?

您认为可复制的生成器根本不是生成器。它们是其他迭代器类的实例。如果要编写自己的迭代器类,则可以:

class MyCount:
    def __init__(self,n=0):
        self._n = n
    def __iter__(self):
        return self
    def __next__(self):
        retval = self._n
        self._n += 1
        return retval

您以这种方式编写的某些迭代器甚至可以合理地复制。其他人,copy.copy会做的事情完全不合理且毫无用处。

,

尽管copy在生成器上没有意义,但是您可以有效地“复制”一个迭代器,以便可以对其进行多次迭代。最简单的方法是使用itertools模块中的tee

def my_count(n=0):
    while True:
        yield n
        n += 1

a,b,c = itertools.tee(my_count(),3)

# now use a,c ...

这将使用内存来缓存迭代器的结果并将其传递。