Autor: Diofeher
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”.
Outra maneira de criar iterables é usando list comprehension:
123456789101112>>> lista=['d','i','o','f','e','h','e','r']>>>forletrainlista:...letra...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:
1lista=[letraforletrain"diofeher"]
Se o objeto for iterável, ele é retornado. Se não, a exceção TypeError é levantada.
12345678910111213141516>>>iter([1,2,3])<listiteratorobjectat0x1004cdc50>>>>iter('diofeher')<iteratorobjectat0x1004cdcd0>>>>iter(2)Traceback (most recent call last):File"<stdin>", line1,in<module>TypeError:'int'objectisnotiterable>>>iter(False)Traceback (most recent call last):File"<stdin>", line1,in<module>TypeError:'bool'objectisnotiterable>>>iter(None)Traceback (most recent call last):File"<stdin>", line1,in<module>TypeError:'NoneType'objectisnotiterable
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.
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.
123456789101112131415161718>>> gerador=(letraforletrain"diofeher")>>> gerador.next()'d'>>> gerador.next()'i'>>>forletraingerador:...letra...ofeher>>> gerador.next()Traceback (most recent call last):File"", line1,inStopIteration
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.
Entendendo como funciona por debaixo dos panos (a parte difícil):
12345678910111213141516171819202122>>>defgerador():...foriinrange(10):...yieldi*2...>>> gera=gerador()>>>gera<generatorobjectgerador at0x1004c8960>>>> gera.next()0>>> gera.next()2>>>foriingera:...i...4681012141618
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:
Uma das referências: http://stackoverflow.com/a/231855/914874
123456789101112131415161718>>>deftest():...yield1...foriinrange(3):...yieldi...>>> testing=test()>>> testing.next()1>>> testing.next()0>>> testing.next()1>>> testing.next()2>>> testing.next()Traceback (most recent call last):File"<stdin>", line1,in<module>StopIteration
Comentários