Explicando iterables, generators e yield no python

12 Apr

Ontem o arthursribeiro perguntou no canal da #ufcg como funcionava o yield do python. Me lembrei quando eu estava iniciando que também tive essa dúvida e tenho certeza que é muito comum entre iniciantes então estou aqui explicando.

Iterables

O primeiro passo para se entender o yield é entender o que são iterables. Um objeto é iterable quando você pode percorrer seus valores usando um “for valor in objeto”.

>>> lista = ['d', 'i', 'o', 'f', 'e', 'h', 'e', 'r']
>>> for letra in lista:
...   print letra
...
d
i
o
f
e
h
e
r

Outra maneira de criar iterables é usando list comprehension:

lista = [letra for letra in "diofeher"]

Geralmente para ser iterable, o objeto precisa ter implementado o método __iter__. Uma regra a essa exceção é a string, que não tem esse método mágico, mas que pode iterada usando seu __getitem__. Uma boa maneira de saber se um objeto é iterável ou não:

>>> iter([1,2,3])
<listiterator object at 0x1004cdc50>
>>> iter('diofeher')
<iterator object at 0x1004cdcd0>
>>> iter(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> iter(False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'bool' object is not iterable
>>> iter(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not iterable

Se o objeto for iterável, ele é retornado. Se não, a exceção TypeError é levantada.

Iterables são úteis porque você pode iterá-los quantas vezes quiser. Uma desvantagem do seu uso é que ele mantém TODA a lista em memória, o que pode não ser útil para grandes listas. É aí que entram os generators.

Generators

Generators são iterables, a diferença é que seus valores são lidos apenas quando é necessário. Pode-se dizer que iterables normais tem eager evalution e generators tem lazy evalution.

>>> gerador = (letra for letra in "diofeher")
>>> gerador.next()
'd'
>>> gerador.next()
'i'
>>> for letra in gerador:
...   print letra
...
o
f
e
h
e
r
>>> gerador.next()
Traceback (most recent call last):
  File "", line 1, in
StopIteration

No exemplo acima usei o generator expression para criar o generator. Você pode percorrer pelos valores de um generator usando o método next(); Ele vai retornar cada valor do generator por vez, até chegar no final; Chegando no fim, se você tenta usar o next(), ele vai levantar uma exceção chamada StopIteration.

Com generators e iterables explicados, posso chegar na dúvida inicial: yield.

Yield

Yield funciona mais ou menos como um return, com a diferença que ele retorna um generator.

>>> def gerador():
...   for i in range(10):
...     yield i * 2
... 
>>> gera = gerador()
>>> print gera
<generator object gerador at 0x1004c8960>
>>> gera.next()
0
>>> gera.next()
2
>>> for i in gera:
...   print i
... 
4
6
8
10
12
14
16
18

Entendendo como funciona por debaixo dos panos (a parte difícil):
Quando você usa a função desse jeito, o código da função não é rodado; O que é retornado é o objeto generator, para o código ser executado somente quando você chama next() ou usa um for no objeto.
Na primeira vez que a sua função for rodada, ela vai rodar do começo e parar até tocar no primeiro yield. Após tocar no primeiro yield, ela vai continuar do ponto que foi parado até achar o próximo yield. Quando não for achado um yield, a exceção StopIteration é lançada. Essa explicação fica melhor vista na função abaixo:

>>> def test():
...   yield 1
...   for i in range(3):
...     yield i
... 
>>> testing = test()
>>> testing.next()
1
>>> testing.next()
0
>>> testing.next()
1
>>> testing.next()
2
>>> testing.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Uma das referências: http://stackoverflow.com/a/231855/914874

Advertisements

10 Responses to “Explicando iterables, generators e yield no python”

  1. Mauro March 20, 2012 at 4:49 pm #

    Muito bom !

  2. Cassio Amador November 6, 2012 at 6:11 pm #

    Obrigado! de todas as páginas que vi (em inglês e português), esta foi a que explicou de maneira mais concisa o que é o “yield”, pelo menos para mim.

  3. Douglas November 26, 2012 at 5:19 pm #

    Muito bom!! Obrigado pelo tópico!

  4. diego March 15, 2013 at 6:55 pm #

    Cara, excelente explicação ! Valeu.

  5. Rayner Gomes August 9, 2013 at 9:40 pm #

    Excelente exemplo! Valeu.

  6. rodrigolira August 26, 2014 at 1:53 pm #

    Reblogged this on Blog do Rodrigo Lira and commented:
    Recomendo! Ótimo post sobre iterable, generators e o uso de yield em Python.

  7. Caio Fabio Costa Ibraim September 23, 2014 at 5:52 pm #

    Achei muito boa a explicação parabéns!

  8. David Sousa December 10, 2014 at 2:52 pm #

    Acho que finalmente entendi isso! Parabéns pelo blog.

  9. Fernando França February 23, 2016 at 7:48 pm #

    Ótimo, muito clara a explicação.

Trackbacks/Pingbacks

  1. Rosalind problema 12 – Enumerating Gene Orders – PERM « Recologia - February 15, 2013

    […] usa python usando a função yield. Foi difícil entender como isso funciona, até que eu vi esse post em outro blog que foi bem […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: