web-dev-qa-db-fra.com

Comment cloner un objet générateur Python?

Considérez ce scénario:

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

 walk = os.walk ('/ home') 

 pour root, dirs, files in walk: 
 pour chemin dans répertoire + fichiers: 
 print os.path.join (root, chemin d'accès) 

 pour root, répertoires, fichiers de la promenade: 
 pour chemin dans répertoire + fichiers: 
 print os.path.join (root, chemin d'accès)

Je sais que cet exemple est un peu redondant, mais vous devriez considérer que nous devons utiliser les mêmes données walk plusieurs fois. J'ai un scénario de référence et l'utilisation des mêmes données walk est obligatoire pour obtenir des résultats utiles.

J'ai essayé walk2 = walk de cloner et d'utiliser à la deuxième itération, mais cela n'a pas fonctionné. La question est ... Comment puis-je le copier? Est-ce jamais possible?

Merci d'avance.

44
Paulo Freitas

Vous pouvez utiliser itertools.tee() :

walk, walk2 = itertools.tee(walk)

Notez que cela pourrait "nécessiter un stockage supplémentaire important", comme le souligne la documentation.

64
Sven Marnach

Définir une fonction

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

Ou même cela

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

Les deux sont utilisés comme ceci:

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

Ceci est un bon cas d'utilisation pour functools.partial() Pour créer une usine-générateur rapide: 

from functools import partial
import os

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

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

Il est difficile de décrire ce que functools.partial() fait avec des mots humains, mais voici ce à quoi il sert. 

Il partial remplit les paramètres de fonction sans exécuter cette fonction. Par conséquent, il agit comme une usine de fonction/générateur.

2
Rob Truxal

Cette réponse vise à développer/développer ce que les autres réponses ont exprimé. La solution variera nécessairement en fonction de ce que exactement vous souhaitez atteindre.

Si vous souhaitez parcourir plusieurs fois le même résultat exact de os.walk, vous devrez initialiser une liste à partir des éléments de os.walk iterable (c'est-à-dire _walk = list(os.walk(path))).

Si vous devez garantir que les données restent les mêmes, c'est probablement votre seule option. Cependant, il existe plusieurs scénarios dans lesquels cela n'est ni possible ni souhaitable.

  1. list() ne sera pas possible si la taille de la sortie est suffisante (par exemple, si vous tentez de list() un système de fichiers entier risque de figer votre ordinateur).
  2. list() n'est pas souhaitable si vous souhaitez acquérir des données "fraîches" avant chaque utilisation.

Dans le cas où list() ne convient pas, vous devrez exécuter votre générateur à la demande. Notez que les générateurs s'éteignent après chaque utilisation, ce qui pose un léger problème. Pour "réexécuter" votre générateur plusieurs fois, vous pouvez utiliser le modèle suivant:

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

Le modèle de conception susmentionné vous permettra de garder votre code au sec.

1
Six