Comment cloner un objet Python generator?

Considérez le scénario suivant:

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

je sais que cet exemple est un peu redondant, mais vous devriez considérer que nous avons besoin d'utiliser le même walk données plus d'une fois. J'ai un scénario de référence et l'utilisation de la même walk données est obligatoire pour obtenir des résultats utiles.

j'ai essayé walk2 = walk à cloner et à utiliser dans la deuxième itération, mais il n'a pas travaillé. La question est de... Comment je peux le copier? Est-il toujours possible?

je vous Remercie à l'avance.

43
demandé sur Zero Piraeus 2011-02-09 15:58:24

5 réponses

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.

59
répondu Sven Marnach 2011-02-09 13:00:03

si vous savez que vous allez itérer à travers le générateur entier pour chaque utilisation, vous obtiendrez probablement la meilleure performance en déroulant le Générateur sur une liste et en utilisant la liste plusieurs fois.

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

13
répondu shang 2011-02-09 13:11:04

Définir une fonction

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

Ou encore

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
répondu S.Lott 2011-02-09 13:24:28

C'est un bon cas d'utilisation pour functools.partial() pour faire un rapide de générateur de l'usine:

from functools import partial
import os

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

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

functools.partial() n'est difficile à décrire avec des mots, mais ce^ est-ce que c'est.

partiellement remplit fonction-params sans exécuter cette fonction. Par conséquent, il agit comme une usine de fonction/générateur.

2
répondu Rob Truxal 2018-07-12 10:40:10

cette réponse vise à étendre/préciser ce que les autres réponses ont exprimé. La solution variera nécessairement selon ce que exactement - vous de l'objectif à atteindre.

si vous voulez itérer sur le même résultat exact de os.walk plusieurs fois, vous devrez initialiser une liste à partir du os.walk itérable les articles (c'est à dire walk = list(os.walk(path))).

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

  1. il ne sera pas possible de list() un itérable si le résultat est de taille suffisante (c.-à-d. en essayant de list() un système de fichiers entier peut geler votre ordinateur).
  2. Il n'est pas souhaitable d' list() un itérable si vous souhaitez acquérir des données" fraîches " avant chaque utilisation.

Dans le cas où list() n'est pas adapté, vous aurez besoin d'exécuter votre générateur sur demande. Notez que les générateurs sont éteints après chaque utilisation, de sorte que cela pose un léger problème. Afin de "réexécuter" votre générateur à plusieurs reprises, 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

susmentionnés modèle de conception vous permettra de garder votre code SEC.

1
répondu Six 2017-01-10 15:03:08