Как клонировать объект генератора Python? - PullRequest
48 голосов
/ 09 февраля 2011

Рассмотрим этот сценарий:

#!/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 клонировать и использовать во второй итерации, но это не сработало,Вопрос в том ... Как я могу это скопировать?Это когда-нибудь возможно?

Заранее спасибо.

Ответы [ 5 ]

66 голосов
/ 09 февраля 2011

Вы можете использовать itertools.tee():

walk, walk2 = itertools.tee(walk)

Обратите внимание, что это может "потребовать значительного дополнительного хранения", как указывается в документации.

14 голосов
/ 09 февраля 2011

Если вы знаете, что собираетесь проходить через весь генератор для каждого использования, вы, вероятно, получите лучшую производительность, развернув генератор в список и используя список несколько раз.

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

4 голосов
/ 09 февраля 2011

Определить функцию

 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)
2 голосов
/ 11 июля 2018

Это хороший вариант использования для 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(), трудно описать человеческими словами, но это то, для чего оно.

Он частично заполняет функциональные параметры без выполнения этой функции. Следовательно, он действует как фабрика функций / генераторов.

1 голос
/ 02 июля 2015

Этот ответ имеет целью расширить / уточнить, что выражали другие ответы. Решение обязательно будет варьироваться в зависимости от того, что точно вы хотите достичь.

Если вы хотите повторять один и тот же результат os.walk несколько раз, вам нужно будет инициализировать список из элементов os.walk iterable (т.е. 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

Вышеупомянутый шаблон дизайна позволит вам сохранить ваш код СУХИМ.

...