如何克隆Python生成器对象?

如何克隆Python生成器对象?,python,object,clone,generator,Python,Object,Clone,Generator,考虑以下情况: #/usr/bin/env python #-*-编码:utf-8-*- 导入操作系统 walk=os.walk(“/home”) 对于根目录、目录和漫游中的文件: 对于dirs+文件中的路径名: 打印os.path.join(根目录,路径名) 对于根目录、目录和漫游中的文件: 对于dirs+文件中的路径名: 打印os.path.join(根目录,路径名) 我知道这个例子有点多余,但是你应该考虑我们不止一次使用同一个 Wave数据。我有一个基准场景,必须使用相同的walk数据才

考虑以下情况:

#/usr/bin/env python
#-*-编码:utf-8-*-
导入操作系统
walk=os.walk(“/home”)
对于根目录、目录和漫游中的文件:
对于dirs+文件中的路径名:
打印os.path.join(根目录,路径名)
对于根目录、目录和漫游中的文件:
对于dirs+文件中的路径名:
打印os.path.join(根目录,路径名)

我知道这个例子有点多余,但是你应该考虑我们不止一次使用同一个<代码> Wave数据。我有一个基准场景,必须使用相同的

walk
数据才能获得有用的结果

我尝试了
walk2=walk
在第二次迭代中克隆和使用,但没有成功。问题是。。。我怎样才能复制它?有可能吗

提前感谢。

您可以使用:

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

定义一个函数

 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)

如果您知道每次使用都要遍历整个生成器,那么通过将生成器展开到列表并多次使用该列表,您可能会获得最佳性能


walk=list(os.walk(“/home”)

此答案旨在扩展/详细说明其他答案所表达的内容。根据您的具体目标,解决方案必然会有所不同

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

如果您必须保证数据保持不变,那么这可能是您唯一的选择。然而,在一些情况下,这是不可能或不可取的

  • 如果输出的大小足够大(即试图
    list()
    整个文件系统可能会冻结您的计算机),则无法
    list()
    一个iterable
  • 如果您希望在每次使用之前获取“新”数据,则不希望
    list()
    iterable
  • 如果
    list()
    不合适,您需要按需运行生成器。请注意,发电机在每次使用后都会熄灭,因此这会带来一个小问题。要多次“重新运行”生成器,可以使用以下模式:

    #!/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
    

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

    这是一个很好的用例 要在工厂制造快速发电机,请执行以下操作:

    from functools import partial
    import os
    
    walk_factory = partial(os.walk, '/home')
    
    walk1, walk2, walk3 = walk_factory(), walk_factory(), walk_factory()
    
    functools.partial()
    的功能很难用人类的语言来描述,但这正是它的用途

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

    此“Python生成器侦听器”代码允许您在单个生成器上有多个侦听器,如
    os.walk
    ,甚至可以让某人稍后“插话”

    def walkme(): os.walk(“/home”)

    m1=多路复用器(walkme) m2=多路复用器(walkme)

    然后m1和m2可以在线程中均匀运行,并在空闲时进行处理

    见:


    使用
    os.walk('/home')
    两次有什么问题?这是一个什么问题?@S.Lott好吧,这种任务在每次运行时变化很大。另一个问题是,在第一次运行之后,系统可能会缓存结果,因此在下一次运行中,我们将得到不精确的结果。我们的想法是走在前面,然后测量两个场景,将其作为参数传递缓存不会导致错误的结果。@pf.me:如果您正在对以下操作进行分析,那么您肯定应该将生成器展开到列表中,以消除目录爬网中的变化(请参阅下面的答案)。但是,如果您正在浏览的目录结构非常大,您可能仍然会因为内存分页而发生变化。@pf.me:“我注意到,在随后的运行中,我会得到随机的结果,相差几秒钟。”如何“克隆”os.walk(“/home”)生成器来解决这个问题呢?虽然这并不是OP提出的确切问题的答案,这是一种很好的方法,无需在内存中存储完整的目录树+1@SvenMarnach:“精确”的问题没有什么意义。你会说定义一个函数“比”
    itertools.tee()
    在这里提到的[…]重要的辅助存储[…]方面更好吗?另外,这个问题说:“一般来说,如果一个迭代器在另一个迭代器启动之前使用了大部分或全部数据,那么使用
    list()
    而不是
    tee()
    ”会更快,因为OP的原始代码段会完全迭代一次,然后再重复一次,是否建议他使用
    list()
    ?使用缓存的生成器,例如,使用所述的
    lambda:a_new_generator
    。另请参阅的注释。为什么我看到这么多人说python中没有克隆生成器的方法?不,伙计,这不是复制生成器,而是将其转换为迭代器…这不是生成器。假设我有一个生成器它从包含60亿行的sql表中部分地、顺序地获取数据……如果我使用itertools.tee,我只是出于好奇而分解我的ram,为什么在生成器中迭代每个对象的必要性使得在迭代之前在内存中保存值映射更有效?
    from functools import partial
    import os
    
    walk_factory = partial(os.walk, '/home')
    
    walk1, walk2, walk3 = walk_factory(), walk_factory(), walk_factory()
    
    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]