Je souhaite modifier le code suivant
for directory, dirs, files in os.walk(directory_1):
do_something()
for directory, dirs, files in os.walk(directory_2):
do_something()
à ce code:
for directory, dirs, files in os.walk(directory_1) + os.walk(directory_2):
do_something()
J'obtiens l'erreur:
type (s) d'opérande non pris en charge pour +: 'générateur' et 'générateur'
Comment joindre deux générateurs en Python?
Réponses:
Je pense que je
itertools.chain()
devrais le faire.la source
itertools.chain()
ne renvoie pas d'types.GeneratorType
instance. Juste au cas où le type exact serait crucial.Un exemple de code:
from itertools import chain def generator1(): for item in 'abcdef': yield item def generator2(): for item in '123456': yield item generator3 = chain(generator1(), generator2()) for item in generator3: print item
la source
itertools.chain()
réponse déjà existante et fortement votée ?En Python (3.5 ou supérieur), vous pouvez faire:
def concat(a, b): yield from a yield from b
la source
def chain(*iterables): for iterable in iterables: yield from iterable
(Mettez ledef
etfor
sur des lignes séparées lorsque vous l'exécutez.)a
est vérifié jusqu'à ce que tout en soit tiré, même s'ilb
ne s'agit pas d'un itérateur. Le faitTypeError
deb
ne pas être un itérateur apparaîtra plus tard.Exemple simple:
from itertools import chain x = iter([1,2,3]) #Create Generator Object (listiterator) y = iter([3,4,5]) #another one result = chain(x, y) #Chained x and y
la source
itertools.chain()
réponse déjà existante et fortement votée ?itertools.chain
renvoie un itérateur, pas un générateur.chain([1, 2, 3], [3, 4, 5])
?Avec itertools.chain.from_iterable, vous pouvez faire des choses comme:
def genny(start): for x in range(start, start+3): yield x y = [1, 2] ab = [o for o in itertools.chain.from_iterable(genny(x) for x in y)] print(ab)
la source
genny
lorsqu'elle renvoie déjà un générateur.list(itertools.chain.from_iterable(genny(x)))
est beaucoup plus concis.list
constructeur est beaucoup plus lisible que la compréhension de la liste. Votre méthode est beaucoup plus illisible à cet égard.Ici, il utilise une expression de générateur avec des
for
s imbriqués :a = range(3) b = range(5) ab = (i for it in (a, b) for i in it) assert list(ab) == [0, 1, 2, 0, 1, 2, 3, 4]
la source
On peut également utiliser l'opérateur de décompression
*
:REMARQUE: fonctionne plus efficacement pour les itérables «non paresseux». Peut également être utilisé avec différents types de compréhension. Le moyen préféré pour le générateur concat serait de la réponse de @Uduse
la source
Si vous voulez garder les générateurs séparés tout en continuant à les parcourir en même temps, vous pouvez utiliser zip ():
REMARQUE: l'itération s'arrête au plus court des deux générateurs
Par exemple:
for (root1, dir1, files1), (root2, dir2, files2) in zip(os.walk(path1), os.walk(path2)): for file in files1: #do something with first list of files for file in files2: #do something with second list of files
la source
Disons que nous devons générer des générateurs (gen1 et gen 2) et que nous voulons effectuer un calcul supplémentaire qui nécessite le résultat des deux. Nous pouvons renvoyer le résultat d'une telle fonction / calcul via la méthode map, qui à son tour renvoie un générateur sur lequel nous pouvons boucler.
Dans ce scénario, la fonction / le calcul doit être implémenté via la fonction lambda. La partie délicate est ce que nous cherchons à faire dans la carte et sa fonction lambda.
Forme générale de la solution proposée:
def function(gen1,gen2): for item in map(lambda x, y: do_somethin(x,y), gen1, gen2): yield item
la source
Je dirais que, comme suggéré dans les commentaires de l'utilisateur "wjandrea", la meilleure solution est
def concat_generators(*args): for gen in args: yield from gen
Il ne change pas le type retourné et est vraiment pythonique.
la source
Mise à jour 2020: fonctionne à la fois en python 3 et python 2
import itertools iterA = range(10,15) iterB = range(15,20) iterC = range(20,25)
### first option for i in itertools.chain(iterA, iterB, iterC): print(i) # 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
### alternative option, introduced in python 2.6 for i in itertools.chain.from_iterable( [iterA, iterB, iterC] ): print(i) # 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
itertools.chain () est la base.
itertools.chain.from_iterables est pratique si vous avez un itérable d'itérables. Par exemple, une liste de fichiers par sous-répertoire comme
[ ["src/server.py", "src/readme.txt"], ["test/test.py"] ]
.la source
Toutes ces solutions compliquées ...
fais juste:
for dir in directory_1, directory_2: for directory, dirs, files in os.walk(dir): do_something()
Si vous voulez vraiment "rejoindre" les deux générateurs, alors faites:
for directory, dirs, files in [ x for osw in [os.walk(directory_1), os.walk(directory_2)] for x in osw ]: do_something()
la source
in
et la fermeture après la fin de la composition de la liste. Indépendamment de cette erreur, je pense que c'est un mauvais exemple à suivre. Il réduit la lisibilité en mélangeant l'indentation. Lesitertools.chain
réponses sont massivement plus lisibles et plus faciles à utiliser.