有没有办法在Python中复制任意生成器?
函数式编程最著名的特性之一是惰性求值和无限列表。在Python中,通常使用生成器实现这些特性。但是函数式编程的规则之一是不可变的,并且生成器不是不可变的。恰恰相反。每次调用生成器上的有没有办法在Python中复制任意生成器?,python,generator,immutability,Python,Generator,Immutability,函数式编程最著名的特性之一是惰性求值和无限列表。在Python中,通常使用生成器实现这些特性。但是函数式编程的规则之一是不可变的,并且生成器不是不可变的。恰恰相反。每次调用生成器上的next(),它都会更改其内部状态 一种可能的解决方法是在调用生成器上的next()之前复制它。这适用于某些生成器,例如count()。(可能count()不是生成器?) 但是如果我定义了自己的生成器,例如,my_count(),我就无法复制它 def my_count(n=0): while True:
next()
,它都会更改其内部状态
一种可能的解决方法是在调用生成器上的next()
之前复制它。这适用于某些生成器,例如count()
。(可能count()
不是生成器?)
但是如果我定义了自己的生成器,例如,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\u gen)
时,我收到一条错误消息:TypeError:cannotpickle-generator对象
有没有办法解决这个问题,或者有其他方法
也许另一种提问方式是:当它复制copy\u gen
时,什么是copy()
复制
谢谢
注意:如果我使用\uuuuuuu iter()
而不是copy()
,则\uuuuuuuu iter()
版本与原始版本一样
my_count_gen = my_count()
my_count_gen_i = my_count_gen.__iter__()
print(next(my_count_gen), 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模块
def my_count(n=0):
while True:
yield n
n += 1
a, b, c = itertools.tee(my_count(), 3)
# now use a, b, c ...
这将使用内存缓存迭代器的结果并将其传递。
count()
不是生成器。如果你认为某个东西是一个生成器,但它是可复制的,那么它就不是生成器。你被一些人误导了,他们用草率的术语来谈论迭代器。这就是正确的术语很重要的原因之一。实际上,您可以尝试使用itertools中的tee
。记住用一个结果副本替换“原始”迭代器。不是所有函数语言都使用延迟求值,也不是只有函数语言才使用延迟求值。谢谢itertools.tee
是一个聪明的解决方案。我没想到@阿拉格尔提出了一个重要的观点。调用tee
时,重要的是返回的迭代器中有一个替换原始迭代器,并且从那时起不再调用原始对象上的next
。我假设OP想要和期望的是类似Clojure的惰性序列的东西——其中包含对序列实例的引用,该序列不是最新的“要存在的项”强制将最早引用状态和最新状态之间的所有项保留在内存中,以供以后重播。给定这个原语,通过保持对无限序列头的引用很容易耗尽内存。。。但是为了与有状态组件(文件句柄、套接字等)进行交互,计算序列中最新的一点才是最重要的……AFAICT,itertools.tee
与该行为非常接近。据我所知,itertools.tee
适用于任意迭代器。因为生成器是迭代器,所以它也应该在生成器上工作。正当只要一个人愿意让所有的tee
ed拷贝yield
具有相同的顺序,那么非本地资源的问题就无关紧要了——因为这似乎就是正在发生的一切。yield
ed结果被缓存以供其他tee
ed副本使用。还是我遗漏了什么?@RussAbbott:你可以使用任何迭代器,如果缓存结果确实是你想要的,而不是复制,那么使用迭代器是有意义的。
def my_count(n=0):
while True:
yield n
n += 1
a, b, c = itertools.tee(my_count(), 3)
# now use a, b, c ...