Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
有没有办法在Python中复制任意生成器?_Python_Generator_Immutability - Fatal编程技术网

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

有没有办法在Python中复制任意生成器?,python,generator,immutability,Python,Generator,Immutability,函数式编程最著名的特性之一是惰性求值和无限列表。在Python中,通常使用生成器实现这些特性。但是函数式编程的规则之一是不可变的,并且生成器不是不可变的。恰恰相反。每次调用生成器上的next(),它都会更改其内部状态 一种可能的解决方法是在调用生成器上的next()之前复制它。这适用于某些生成器,例如count()。(可能count()不是生成器?) 但是如果我定义了自己的生成器,例如,my_count(),我就无法复制它 def my_count(n=0): while True:

函数式编程最著名的特性之一是惰性求值和无限列表。在Python中,通常使用生成器实现这些特性。但是函数式编程的规则之一是不可变的,并且生成器不是不可变的。恰恰相反。每次调用生成器上的
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 ...