Comportamento do iterador de lista do Python e próximo (iterador)

147

Considerar:

>>> lst = iter([1,2,3])
>>> next(lst)
1
>>> next(lst)
2

Portanto, o avanço do iterador é, como esperado, tratado pela mutação desse mesmo objeto.

Sendo esse o caso, eu esperaria:

a = iter(list(range(10)))
for i in a:
   print(i)
   next(a)

para pular cada segundo elemento: a chamada para nextdeve avançar o iterador uma vez; a chamada implícita feita pelo loop deve avançar uma segunda vez - e o resultado dessa segunda chamada seria atribuído a i.

Não faz. O loop imprime todos os itens da lista, sem pular nenhum.

Meu primeiro pensamento foi que isso poderia acontecer porque o loop solicita itero que é passado e isso pode fornecer um iterador independente - esse não é o caso, como temos iter(a) is a.

Então, por que nextnão parece avançar o iterador nesse caso?

lvc
fonte

Respostas:

197

O que você vê é o intérprete repetindo o valor de retorno, next()além de iser impresso a cada iteração:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    next(a)
... 
0
1
2
3
4
5
6
7
8
9

Assim 0é a saída de print(i), 1o valor de retorno de next(), ecoado pelo intérprete interativo, etc. Existem apenas 5 iterações, cada iteração resultando em duas linhas sendo gravadas no terminal.

Se você atribuir a saída das next()coisas, funcione conforme o esperado:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    _ = next(a)
... 
0
2
4
6
8

ou imprima informações extras para diferenciar a print()saída do eco do interpretador interativo:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print('Printing: {}'.format(i))
...    next(a)
... 
Printing: 0
1
Printing: 2
3
Printing: 4
5
Printing: 6
7
Printing: 8
9

Em outras palavras, next()está funcionando conforme o esperado, mas como retorna o próximo valor do iterador, ecoado pelo intérprete interativo, você é levado a acreditar que o loop possui sua própria cópia do iterador de alguma forma.

Martijn Pieters
fonte
13
Eu não estava ciente desse comportamento do intérprete. Fico feliz por ter descoberto isso antes de perder muito tempo pensando sobre isso enquanto resolvia algum problema real.
Brandizzi
5
... *morre*. O pior de tudo é que me lembro de mencionar exatamente esse comportamento de intérprete para alguém talvez uma semana atrás.
lvc
interessante. Eu tentei para i em um: próximo (a); imprimai e pensei que iria pular para 1 e imprimir 1,3,5,7,9. Mas ainda é 0,2,4,6,8. Por quê?
user2290820
3
i foi atribuído. next(a)significa que a próxima iteração 2está atribuída ie você avança a, imprime ietc.
Martijn Pieters
1
Isso não funciona se n for ímpar - a StopIterationexcreção next(a)é aumentada quando é chamada após o esgotamento da lista.
Raf
13

O que está acontecendo é que next(a)retorna o próximo valor de a, que é impresso no console porque não é afetado.

O que você pode fazer é afetar uma variável com este valor:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    b=next(a)
...
0
2
4
6
8
njzk2
fonte
8

I encontrar as respostas existentes um pouco confuso, porque apenas indiretamente indicam a coisa mistificadora essencial no exemplo de código: ambos * o "print i" eo "próximo (a)" estão fazendo com que os seus resultados a ser impresso.

Como eles estão imprimindo elementos alternativos da sequência original e é inesperado que a instrução "next (a)" esteja sendo impressa, parece que a instrução "print i" está imprimindo todos os valores.

Nessa perspectiva, fica mais claro que atribuir o resultado de "next (a)" a uma variável inibe a impressão do resultado ', de modo que apenas os valores alternativos que a variável de loop "i" são impressos. Da mesma forma, fazer a declaração "print" emitir algo mais distinto também a desambigua.

(Uma das respostas existentes refuta as outras porque essa resposta está tendo o código de exemplo avaliado como um bloco, para que o intérprete não esteja relatando os valores intermediários para "next (a)".)

A coisa sedutora em responder perguntas, em geral, está sendo explícita sobre o que é óbvio quando você conhece a resposta. Pode ser ilusório. Da mesma forma, criticar as respostas quando você as entender. É interessante...

klm
fonte
2

Algo está errado com o seu Python / Computador.

a = iter(list(range(10)))
for i in a:
   print(i)
   next(a)

>>> 
0
2
4
6
8

Funciona como esperado.

Testado em Python 2.7 e em Python 3+. Funciona corretamente em ambos

Inbar Rose
fonte
5
Eu obter o mesmo resultado que @lvc (apenas em IDLE no entanto, quando executado como script eu recebo este))
jamylak
3
@Inbar Rose Somente se você executar como script.
Quintec
1
é o comportamento de colocar código via shell interativo. Se a função retorna um valor sem ser utilizado, intérprete seria imprimi-lo a desembolsar como saída de depuração
Reishin
2

Para quem ainda não entende.

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    next(a)
... 
0 # print(i) printed this
1 # next(a) printed this
2 # print(i) printed this
3 # next(a) printed this
4 # print(i) printed this
5 # next(a) printed this
6 # print(i) printed this
7 # next(a) printed this
8 # print(i) printed this
9 # next(a) printed this

Como outros já disseram, nextaumenta o iterador em 1 conforme o esperado. Atribuir seu valor retornado a uma variável não altera magicamente seu comportamento.

Wesley
fonte
1

Ele se comporta da maneira que você deseja se chamado como uma função:

>>> def test():
...     a = iter(list(range(10)))
...     for i in a:
...         print(i)
...         next(a)
... 
>>> test()
0
2
4
6
8
burmer
fonte