如何克隆Python生成器对象?

时间:2011-02-09 12:58:24

标签: python object clone generator

考虑这种情况:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os

walk = os.walk('/home')

for root, dirs, files in walk:
    for pathname in dirs+files:
        print os.path.join(root, pathname)

for root, dirs, files in walk:
    for pathname in dirs+files:
        print os.path.join(root, pathname)

我知道这个示例有点多余,但您应该考虑我们需要多次使用相同的walk数据。我有一个基准测试场景,必须使用相同的walk数据才能获得有用的结果。

我已经尝试walk2 = walk克隆并在第二次迭代中使用,但它没有用。问题是......我怎么能复制它?它有可能吗?

提前谢谢。

6 个答案:

答案 0 :(得分:66)

您可以使用itertools.tee()

walk, walk2 = itertools.tee(walk)

请注意,这可能“需要大量的额外存储空间”,正如文档所指出的那样。

答案 1 :(得分:14)

如果你知道你将为每次使用迭代整个生成器,你可能会通过将生成器展开到列表并多次使用列表来获得最佳性能。

walk = list(os.walk('/home'))

答案 2 :(得分:4)

定义一个函数

 def walk_home():
     for r in os.walk('/home'):
         yield r

甚至是这个

def walk_home():
    return os.walk('/home')

两者都是这样使用的:

for root, dirs, files in walk_home():
    for pathname in dirs+files:
        print os.path.join(root, pathname)

答案 3 :(得分:2)

这是functools.partial()的好用例 建立快速的发电机工厂:

--hot

from functools import partial import os walk_factory = partial(os.walk, '/home') walk1, walk2, walk3 = walk_factory(), walk_factory(), walk_factory() 的作用很难用人类的语言来形容,但这是它的作用。

部分填写功能参数,而不执行该功能。因此,它充当了函数/生成器工厂。

答案 4 :(得分:1)

这个答案旨在扩展/阐述其他答案所表达的内容。解决方案必然会根据您希望实现的完全而变化。

如果要多次迭代os.walk的完全相同的结果,则需要从os.walk iterable的项目(即walk = list(os.walk(path)))初始化列表。

如果您必须保证数据保持不变,那么这可能是您唯一的选择。但是,有几种情况是不可能或不可取的。

  1. 如果输出的大小足够list(),则list()不可能(即尝试list()整个文件系统可能会冻结您的计算机。)
  2. 如果您希望在每次使用之前获取“新鲜”数据,那么list()是不可取的。
  3. 如果#!/usr/bin/env python # -*- coding: utf-8 -*- import os class WalkMaker: def __init__(self, path): self.path = path def __iter__(self): for root, dirs, files in os.walk(self.path): for pathname in dirs + files: yield os.path.join(root, pathname) walk = WalkMaker('/home') for path in walk: pass # do something... for path in walk: pass 不合适,您需要按需运行生成器。请注意,每次使用后发电机都会熄灭,因此这会产生轻微问题。为了多次“重新运行”您的生成器,您可以使用以下模式:

    SSH -L 3307:mysql:3306 <server_name>

    上述设计模式将允许您将代码保持干净。

答案 5 :(得分:0)

此“ Python生成器侦听器”代码可让您在单个生成器上拥有多个侦听器,例如os.walk,甚至以后还会有人“鸣叫”。

def walkme():    os.walk('/ home')

m1 =混合器(walkme) m2 = Muxer(walkme)

然后m1和m2甚至可以在线程中运行,并且可以随意处理。

请参阅:https://gist.github.com/earonesty/cafa4626a2def6766acf5098331157b3

import queue
from threading import Lock
from collections import namedtuple

class Muxer():
    Entry = namedtuple('Entry', 'genref listeners, lock')

    already = {}
    top_lock = Lock()

    def __init__(self, func, restart=False):
        self.restart = restart
        self.func = func
        self.queue = queue.Queue()

        with self.top_lock:
            if func not in self.already:
                self.already[func] = self.Entry([func()], [], Lock())
            ent = self.already[func]

        self.genref = ent.genref
        self.lock = ent.lock
        self.listeners = ent.listeners

        self.listeners.append(self)

    def __iter__(self):
        return self

    def __next__(self):
        try:
            e = self.queue.get_nowait()
        except queue.Empty:
            with self.lock:
                try:
                    e = self.queue.get_nowait()
                except queue.Empty:
                    try:
                        e = next(self.genref[0])
                        for other in self.listeners:
                            if not other is self:
                                other.queue.put(e)
                    except StopIteration:
                        if self.restart:
                            self.genref[0] = self.func()
                        raise
        return e

    def __del__(self):
        with self.top_lock:
            try:
                self.listeners.remove(self)
            except ValueError:
                pass
            if not self.listeners and self.func in self.already:
                del self.already[self.func]