Quando se lida com a iteração é recorrente surgirem determinadas dúvidas. Vejamos algumas delas.

Voltando à lista de números (ver post Ciclos em Python, o básico). Sabemos que podemos iterar sobre os valores da lista sem os indexar, mas por vezes podemos precisar mesmo de ter o índice dos valores.

Problemas


Forma incorrecta de fazer
for i in range(len(my_list)):
    v = my_list[i]
    print i, v


Nesse caso poderíamos voltar a usar o range(len(my_list)) do exemplo 2. Mas, na linguagem de programação Python, existe uma melhor forma de o fazer, utilizando a função enumerate().


Forma correcta de fazer
for i, v in enumerate(my_list):
    print i, v


No código acima vemos como podemos utilizar a função enumerate() para obter dois valores em cada iteração. O i é o índice do valor e o v é o valor propriamente dito. Desta forma obtemos os valores com os seus índices sem ter que iterar sobre inteiros.


cidades = ['Aveiro', 'Braga', 'Lisboa', 'Porto']
list(enumerate(cidades))

[(0, 'Aveiro'), (1, 'Braga'), (2, 'Lisboa'), (3, 'Porto')]

Além de podermos iterar sobre os iteráveis podemos também manipular os iteráveis para produzir novos iteráveis que nos sejam mais convenientes. A função enumerate() pega num iterável e transforma-o num conjunto de pares, cada valor do iterável é associado a um número sequencial a começar no zero.

No exemplo acima, a lista de cidades é chamada com a função enumerate() que associa um número sequencial a cada cidade. A função list() dá-nos uma nova lista com o emparelhamento das cidades e os respectivos números.

for num, cidade in enumerate(cidades):
    print num, cidade

0 Aveiro
1 Braga
2 Lisboa
3 Porto

Podemos usar a atribuição múltipla para obter os pares como dois valores separados. No exemplo acima às variáveis num e cidade são atribuídas os valores dos números das cidades e o nome das cidades provenientes da lista.


A vantagem de usar a função enumerate() é que ela funciona com qualquer iterável. Quando usamos índices para iterar sobre um iterável é necessário que o iterável suporte índices o que nem sempre acontece. Não é possível aceder, por exemplo, à 10ª linha de um ficheiro.

Além do enumerate() funcionar com qualquer iterável é também mais compacto e directo.


i=0
for v in iteravel:
    print i, v
    i += 1


Outra forma comum de obter o índice de um iterável é manter um contador à parte, mas isso é como ter um atrelado agarrado a nós. Esta técnica funciona mas adiciona complexidade extra ao ciclo. Esta é uma forma mais difícil de ler o código e no caso do ciclo ser um pouco mais extenso do que este, o contador pode facilmente ficar esquecido ou fora do lugar em futuras alterações ao código. Quanto mais simples for o ciclo melhor. Menos partes a mexer significa menos bugs.



Os próximos posts continuarão a ser sobre ciclos em Python.


Este post faz parte da série de posts sobre Ciclos e Interáveis em Python:
  1. Ciclos em Python, o básico
  2. Ciclos em Python e os Iteráveis
  3. Ciclos em Python, mais exemplos de Iteráveis
  4. Ciclos em Python, uso de Iteráveis fora dos ciclos
  5. Ciclos em Python, problemas comuns e os índices (post actual)
  6. Ciclos em Python, iterar sobre duas listas
  7. Ciclos em Python, iteração personalizada
  8. Ciclos em Python, Generators - parte 1
  9. Ciclos em Python, Generators - parte 2
  10. Ciclos em Python, Generators - parte 3
  11. Ciclos em Python, operações de baixo nível
  12. Ciclos em Python, como tornar os nossos objectos em Iteráveis
  13. Ciclos em Python, conclusão


A inspiração para este post veio daqui.