web-dev-qa-db-ja.com

Pythonジェネレーターオブジェクトを複製する方法は?

このシナリオを考えてみましょう:

#!/ usr/bin/env python 
#-*-coding:utf-8-*-
 import os 
 
 walk = os.walk( ' /home')

walk、dirs、dirsのファイルの場合:
 dirs + filesのパス名の場合:
 print os.path.join(root、pathname )
 
 root、dirs、ウォークインファイルの場合:
 dirs + filesのパス名の場合:
 print os.path.join(root、pathname)

この例は多少冗長であることは知っていますが、同じwalkデータを複数回使用する必要があることを考慮する必要があります。私はベンチマークシナリオを持っています。同じwalkデータの使用は、役立つ結果を得るために必須です。

私はもう試した walk2 = walkを複製して2回目の反復で使用しますが、機能しませんでした。問題は...どうすればコピーできますか?可能ですか?

前もって感謝します。

54
Paulo Freitas

itertools.tee() を使用できます:

walk, walk2 = itertools.tee(walk)

ドキュメンテーションが指摘するように、これは「かなりの追加ストレージを必要とする」かもしれないことに注意してください。

67
Sven Marnach

すべての使用法についてジェネレーター全体を反復処理することがわかっている場合は、ジェネレーターをリストにアンロールし、リストを複数回使用すると、おそらく最高のパフォーマンスが得られます。

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

14
shang

関数を定義する

 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)
4
S.Lott

これは、 functools.partial() を使用して、ジェネレーターファクトリーをすばやく作成するのに適した例です。

_from functools import partial
import os

walk_factory = partial(os.walk, '/home')

walk1, walk2, walk3 = walk_factory(), walk_factory(), walk_factory()
_

functools.partial()が人間の言葉で説明するのは難しいですが、これはそのためです。

関数の一部は、その関数を実行せずにfunction-paramsに入力します。したがって、関数/ジェネレータファクトリとして機能します。

2
Rob Truxal

この回答は、他の回答が表明した内容を拡張/詳しく説明することを目的としています。解決策は必然的に、達成したい正確にによって異なります。

_os.walk_のまったく同じ結果を複数回繰り返したい場合は、_os.walk_イテラブルの項目(つまり、walk = list(os.walk(path)))からリストを初期化する必要があります。

データが同じであることを保証する必要がある場合は、それがおそらく唯一の選択肢です。ただし、これが不可能または望ましくないシナリオがいくつかあります。

  1. 出力が十分なサイズの場合、反復可能オブジェクトをlist()することはできません(つまり、ファイルシステム全体をlist()しようとすると、コンピューターがフリーズする可能性があります)。
  2. 毎回使用する前に「新しい」データを取得したい場合は、反復可能関数をlist()することは望ましくありません。

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
_

前述の設計パターンにより、コードをDRYに保つことができます。

1
Six

この「Pythonジェネレーターリスナー」コードを使用すると、os.walkのように、単一のジェネレーターに多数のリスナーを設定したり、後で誰かが "チャイムイン"したりすることができます。

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

m1 = Muxer(walkme)m2 = Muxer(walkme)

次に、m1とm2はスレッドで実行され、自由に処理できます。

参照: https://Gist.github.com/earonesty/cafa4626a2def6766acf5098331157b

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]
0
Erik Aronesty