python object clone generator

¿Cómo clonar un objeto generador de Python?



object clone (5)

Considera este escenario:

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

Sé que este ejemplo es un poco redundante, pero debería considerar que necesitamos usar los mismos datos de walk más de una vez. Tengo un escenario de referencia y el uso de los mismos datos de walk es obligatorio para obtener resultados útiles.

Intenté walk2 = walk para clonar y usar en la segunda iteración, pero no funcionó. La pregunta es ... ¿Cómo puedo copiarlo? ¿Es alguna vez posible?

Gracias de antemano.


Definir una función

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

O incluso esto

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

Ambos se usan así:

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


Esta respuesta tiene como objetivo extender / elaborar sobre lo que han expresado las otras respuestas. La solución variará necesariamente según lo que se pretenda lograr exactamente .

Si desea os.walk varias veces el mismo resultado exacto de os.walk , deberá inicializar una lista de los os.walk iterables de os.walk (es decir, walk = list(os.walk(path)) ).

Si debe garantizar que los datos siguen siendo los mismos, esa es probablemente su única opción. Sin embargo, hay varios escenarios en los que esto no es posible o deseable.

  1. No será posible list() un iterable si el resultado es de tamaño suficiente (es decir, intentar list() un sistema de archivos completo puede congelar su computadora).
  2. No es deseable list() un iterable si desea adquirir datos "nuevos" antes de cada uso.

En el caso de que la list() no sea adecuada, deberá ejecutar su generador bajo demanda. Tenga en cuenta que los generadores se extinguen después de cada uso, por lo que esto plantea un ligero problema. Para poder "volver a ejecutar" tu generador varias veces, puedes usar el siguiente patrón:

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

El patrón de diseño mencionado anteriormente le permitirá mantener su código SECO.


Este es un buen caso de uso para functools.partial() para hacer una fábrica de generador rápida:

from functools import partial import os walk_factory = partial(os.walk, ''/home'') walk1, walk2, walk3 = walk_factory(), walk_factory(), walk_factory()

Lo que functools.partial() hace es difícil de describir con palabras humanas, pero esto ^ es para lo que es.

Rellena parcialmente los parámetros de función sin ejecutar esa función. En consecuencia, actúa como una fábrica de función / generador.


Puedes usar itertools.tee() :

walk, walk2 = itertools.tee(walk)

Tenga en cuenta que esto podría "necesitar almacenamiento adicional significativo", como lo señala la documentación.


Si sabe que va a iterar a través de todo el generador para cada uso, probablemente obtendrá el mejor rendimiento desenrollando el generador a una lista y utilizando la lista varias veces.

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