2017-09-27 6 views
0

Je tombe sur ce code de pymotw.com dans la section de fusion et de fractionnement.Comment deux déclarations de rendement consécutives fonctionnent en python?

from itertools import * 


def make_iterables_to_chain(): 
    yield [1, 2, 3] 
    yield ['a', 'b', 'c'] 


for i in chain.from_iterable(make_iterables_to_chain()): 
    print(i, end=' ') 
print() 

Je n'arrive pas à comprendre comment fonctionne make_iterables_to_chain(). Il contient deux déclaration de rendement, comment ça marche? Je sais comment fonctionnent les générateurs mais là, mais il n'y avait qu'une seule instruction rendement.

Aide, s'il vous plaît!

+0

Il n'était probablement qu'une seule déclaration de rendement, mais est-ce pas dans une boucle? –

+0

il y en a deux. Je n'ai tout simplement pas compris le fondamental de deux rendements. Je pensais qu'une fois le rendement rencontré, il ne va pas à d'autres rendements, mais ce n'est pas le cas. – ispeedster

Répondre

0

De la même manière qu'un seul yield fonctionne.

Vous pouvez avoir autant de yield s que vous le souhaitez dans un générateur, quand __next__ est appelé, il s'exécutera jusqu'à ce qu'il se heurte au rendement suivant. Vous récupérez ensuite l'expression générée et le générateur marque une pause jusqu'à ce que la méthode __next__ soit invoquée à nouveau.

Exécuter quelques next appels sur le générateur pour voir ceci:

>>> g = make_iterables_to_chain() # get generator 
>>> next(g) # start generator, go to first yield, get result 
[1, 2, 3] 
>>> next(g) # resume generator, go to second yield, get result 
['a', 'b', 'c'] 
>>> # next(g) raises Exception since no more yields are found 
0

Un générateur permet effectivement une fonction pour revenir plusieurs fois. Chaque fois qu'une instruction yield est exécutée, la valeur est renvoyée à l'appelant et l'appelant peut poursuivre l'exécution de la fonction.

Habituellement, ils sont utilisés comme itérables dans les boucles for.

La fonction suivante incrémente chaque élément dans un itérable d'un montant:

def inc_each(nums, inc): 
    for i in nums: 
     yield i + inc 

Voici un exemple de l'utilisation:

gen = inc_each([1, 2, 3, 4], 100) 
print(list(gen)) # [101, 102, 103, 104] 

list est utilisé ici pour convertir un itérables arbitraire (dans ce cas un générateur) à une liste.

La fonction que vous décrivez deux déclarations exécute rendement:

def make_iterables_to_chain(): 
    yield [1, 2, 3] 
    yield ['a', 'b', 'c'] 

Si vous l'appelez, il retourne un générateur qui, si itérés, donne les listes [1, 2, 3] et ['a', 'b', 'c'].

gen = make_iterables_to_chain() 
print(list(gen)) # [[1, 2, 3], ['a', 'b', 'c']] 

itertools.chain.from_iterable prendra un (éventuellement infini) itérables de iterables et « aplatir » il, un retour (possible infini) itérables que le résultat.

Voici une façon dont il pourrait être mis en œuvre:

def from_iterable(iterables): 
    for iterable in iterables: 
     for i in iterable: 
      yield i 
+0

voulez-vous dire que lorsque la première fois make_iterables_to_chain() est appelée, elle donne la première déclaration de rendement, c'est-à-dire [1,2,3] et lorsque la deuxième fois elle est appelée la seconde déclaration de rendement est appelée i.e.['a', 'b', 'c'] – ispeedster

+0

@ispeedster Les expressions de générateur ont en réalité une couche supplémentaire d'indirection. Lorsque vous appelez 'make_iterables_to_chain', il retourne un * objet générateur * qui peut être itéré en utilisant' iter() 'et' next() '(bien que cela soit généralement géré automatiquement par les boucles' for'). Ainsi, la fonction elle-même n'est appelée qu'une seule fois, mais l'objet résultant peut être itéré à travers et peut donner plusieurs valeurs. – Challenger5

+0

merci, je l'ai comment ça marche. – ispeedster