46 votes

Python: syntaxe de compréhension de liste imbriquée avancée

J'ai été jouer avec les interprétations de la liste pour obtenir une meilleure compréhension d'eux et j'ai couru dans certains inattendus de sortie que je ne suis pas capable de l'expliquer. Je n'ai pas trouvé cette question avant, mais si il /est/ a répéter la question, je m'en excuse.

J'ai été essentiellement à essayer d'écrire un générateur qui a généré des générateurs. Un simple générateur qui utilise compréhension de liste devrait ressembler à ceci:

(x for x in range(10) if x%2==0) # generates all even integers in range(10)

Ce que j'essayais de faire, c'était écrire un générateur qui produit deux générateurs - la première de ce qui a généré le même nombre dans la gamme(10) et la deuxième qui a généré les nombres impairs in range(10). Pour cela, j'ai fait:

>>> (x for x in range(10) if x%2==i for i in range(2))
<generator object <genexpr> at 0x7f6b90948f00>

>>> for i in g.next(): print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <genexpr>
UnboundLocalError: local variable 'i' referenced before assignment
>>> g.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> g = (x for x in range(10) if x%2==i for i in range(2))
>>> g
<generator object <genexpr> at 0x7f6b90969730>
>>> g.next()
Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 1, in <genexpr>
    UnboundLocalError: local variable 'i' referenced before assignment

Je ne comprends pas pourquoi " je " est référencé avant affectation

J'ai pensé qu'il pourrait y avoir eu quelque chose à voir avec i in range(2), j'ai donc fait:

>>> g = (x for x in range(10) if x%2==i for i in [0.1])
>>> g
<generator object <genexpr> at 0x7f6b90948f00>
>>> g.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <genexpr>
UnboundLocalError: local variable 'i' referenced before assignment

Cela n'a pas de sens pour moi, donc j'ai pensé qu'il valait mieux essayer quelque chose de plus simple en premier. Je suis donc rentré à des listes et essayé:

>>> [x for x in range(10) if x%2==i for i in range(2)]
[1, 1, 3, 3, 5, 5, 7, 7, 9, 9]

qui je devrait être la même chose que:

>>> l = []
>>> for i in range(2):
...     for x in range(10):
...             if x%2==i:
...                     l.append(x)
... 
>>> l
[0, 2, 4, 6, 8, 1, 3, 5, 7, 9] # so where is my list comprehension malformed?

Mais quand je l'ai essayé sur une intuition, cela a fonctionné:

>>> [[x for x in range(10) if x%2==i] for i in range(2)]
[[0, 2, 4, 6, 8], [1, 3, 5, 7, 9]] # so nested lists in nested list comprehension somehow affect the scope of if statements? :S

J'ai donc pensé qu'il pourrait être un problème avec ce niveau de la portée de l' if déclaration intervient. J'ai donc essayé ceci:

>>> [x for x in range(10) for i in range(2) if x%2==i]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Et maintenant, je suis confus. Quelqu'un peut-il m'expliquer ce comportement. Je ne comprends pas pourquoi mes interprétations de la liste semblent être mal formé, je ne comprends comment la détermination de la portée de l' if des déclarations de travaux.

Toute aide serait grandement appréciée

Merci

PS: bien que la preuve-la lecture de la question, j'ai réalisé que cela ressemble un peu à des devoirs à faire à la question - il n'est pas.

46voto

Lie Ryan Points 24517

vous devez utiliser des parenthèses:

((x for x in range(10) if x%2==i) for i in range(2))

Cela n'a pas de sens pour moi, donc je pensé qu'il valait mieux essayer quelque chose de plus simple d'abord. Je suis donc retourné à la liste et essayé:

[>>> [x for x in range(10) si x%2==i for i in range(2)] [1, 1, 3, 3, 5, 5, 7, 7, 9, 9]

Qui a travaillé, parce que le précédent compréhension de liste les fuites de la variable i à l'affichage de la portée, et de devenir le je pour de l'actuel. Essayez de démarrer une nouvelle interpréteur python, et qui ont échoué en raison d'NameError. Le comptoir de la fuite de comportement a été supprimé en Python 3.

EDIT:

L'équivalent pour la boucle pour:

(x for x in range(10) if x%2==i for i in range(2))

serait:

l = []
for x in range(10):
    if x%2 == i:
        for i in range(2):
            l.append(x)

ce qui donne aussi une erreur de nom.

EDIT2:

le parenthesed version:

((x for x in range(10) if x%2==i) for i in range(2))

est équivalent à:

li = []
for i in range(2):
    lx = []
    for x in range(10):
        if x%2==i:
            lx.append(x)
    li.append(lx)

10voto

L'équivalent for-loop de Lie Ryan m'amène à ce qui suit, qui semble très bien fonctionner:

 [x for i in range(2) for x in range(10) if i == x%2]
 

les sorties

 [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
 

7voto

Duncan Points 25356

L'expansion sur le Mensonge de Ryan répondre un peu:

quelque chose = (x for x in range(10) si x%2==i for i in range(2))

est équivalent à:

def _gen1():
    for x in range(10):
        if x%2 == i:
            for i in range(2):
                yield x
something = _gen1()

alors que la parenthesised version est équivalent à:

def _gen1():
    def _gen2():
        for x in range(10):
            if x%2 == i:
                yield x

    for i in range(2):
        yield _gen2()
something = _gen1()

De ce fait, le rendement des deux générateurs:

[<generator object <genexpr> at 0x02A0A968>, <generator object <genexpr> at 0x02A0A990>]

Malheureusement, les générateurs de il les rendements sont un peu instables comme le résultat dépendra de votre façon de consommer:

>>> gens = ((x for x in range(10) if x%2==i) for i in range(2))
>>> for g in gens:
        print(list(g))

[0, 2, 4, 6, 8]
[1, 3, 5, 7, 9]
>>> gens = ((x for x in range(10) if x%2==i) for i in range(2))
>>> for g in list(gens):
        print(list(g))

[1, 3, 5, 7, 9]
[1, 3, 5, 7, 9]

Mon conseil est d'écrire le générateur fonctionne à plein: je pense que d'essayer d'obtenir le bon cadrage sur i sans faire de qui peut être tout simplement impossible.

5voto

Glenn Maynard Points 24451

Lie a la réponse à la question syntaxique. Une suggestion: ne pas trop fourrer dans le corps d'un générateur. Une fonction est beaucoup plus lisible.

 def make_generator(modulus):
    return (x for x in range(10) if x % 2 == modulus)
g = (make_generator(i) for i in range(2))
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X