• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Iteraveis e geradores
 

Iteraveis e geradores

on

  • 889 views

Como funciona iteração em Python; o padrão de projeto Iterator e o conceito de gerador

Como funciona iteração em Python; o padrão de projeto Iterator e o conceito de gerador

Statistics

Views

Total Views
889
Views on SlideShare
880
Embed Views
9

Actions

Likes
2
Downloads
36
Comments
0

1 Embed 9

https://twitter.com 9

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Iteraveis e geradores Iteraveis e geradores Presentation Transcript

    • Luciano Ramalho luciano@ramalho.org @ramalhoorgIteráveis, geradores & cia:o caminho pythonico
    • Comparando: C e Python#include <stdio.h>int main(int argc, char *argv[]) { int i; for(i = 0; i < argc; i++) printf("%sn", argv[i]); return 0;} import sys for arg in sys.argv: print arg @ramalhoorg
    • Iteração em Javaclass Argumentos { public static void main(String[] args) { for (int i=0; i < args.length; i++) System.out.println(args[i]); }} $ java Argumentos alfa bravo charlie alfa bravo charlie @ramalhoorg
    • Iteração em Java ≥1.5 ano: 2004 • Enhanced for (for melhorado)class Argumentos2 { public static void main(String[] args) { for (String arg : args) System.out.println(arg); }} $ java Argumentos2 alfa bravo charlie alfa bravo charlie @ramalhoorg
    • Iteração em Java ≥1.5 ano: 2004 • Enhanced for (for melhorado)class Argumentos2 { public static void main(String[] args) { for (String arg : args) System.out.println(arg); }} ano: import sys 1991 for arg in sys.argv: print arg @ramalhoorg
    • Exemplos de iteração• Iteração em Python não se limita a tipos primitivos• Exemplos • string • arquivo • Django QuerySet @ramalhoorg
    • >>> from django.db import connection>>> q = connection.queries>>> q[]>>> from municipios.models import *>>> res = Municipio.objects.all()[:5]>>> q[]>>> for m in res: print m.uf, m.nome...GO Abadia de Goiás demonstração:MG Abadia dos DouradosGO Abadiânia queryset é umMG Abaeté iterável lazyPA Abaetetuba>>> q[{time: 0.000, sql: uSELECT"municipios_municipio"."id", "municipios_municipio"."uf","municipios_municipio"."nome","municipios_municipio"."nome_ascii","municipios_municipio"."meso_regiao_id","municipios_municipio"."capital","municipios_municipio"."latitude","municipios_municipio"."longitude","municipios_municipio"."geohash" FROM "municipios_municipio"ORDER BY "municipios_municipio"."nome_ascii" ASC LIMIT 5}]
    • Em Python o comando foritera sobre... “iteráveis”• Definicão preliminar informal: • “iterável” = que pode ser iterado • assim como: “desmontável” = que pode ser desmontado• Iteráveis podem ser usados em outros contextos além do laço for @ramalhoorg
    • List comprehensionList comprehensions ● Compreensão de lista ou abrangência ● Exemplo: usar todos os elementos:• Expressões que consomem L2 = [n*10 for n in L] – iteráveis e produzem listas qualquer iterável resultado: uma lista>>> s = abracadabra>>> l = [ord(c) for c in s]>>> [ord(c) for c in s][97, 98, 114, 97, 99, 97, 100, 97, 98, 114, 97] ≈ notação matemática de conjuntos @ramalhoorg
    • Set & dict comprehensions• Expressões que consomem iteráveis e produzem sets ou dicts >>> s = abracadabra >>> {c for c in s} set([a, r, b, c, d]) >>> {c:ord(c) for c in s} {a: 97, r: 114, b: 98, c: 99, d: 100} @ramalhoorg
    • Tipos iteráveis embutidos • basestring • frozenset • str • list • unicode • set • dict • tuple • file • xrange @ramalhoorg
    • Funções embutidas queconsomem iteráveis• all • max• any • min• filter • reduce• iter • sorted• len • sum• map • zip @ramalhoorg
    • Operações com iteráveis >>> a, b, c = XYZ • Desempacotamento >>> X a de tupla >>> b Y • em atribuições >>> Z >>> c g = (n for n in [1, 2, 3]) • em chamadas de funções >>> >>> a, b, c = g a>>> def soma(a, b): 1... return a + b >>> b... 2>>> soma(1, 2) >>> c3 3>>> t = (3, 4)>>> soma(t)Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: soma() takes exactly 2 arguments (1 given)>>> soma(*t) @ramalhoorg7
    • Em Python, um iterável é...• Um objeto a partir do qual a função iter consegue obter um iterador.• A chamada iter(x): • invoca x.__iter__() para obter um iterador • ou, se x.__iter__ não existe: • fabrica um iterador que acessa os itens de x sequenciamente: x[0], x[1], x[2] etc. @ramalhoorg
    • Trem:uma sequência de vagões trem trem[0] Curiosidade: sequências eram chamadas “trains” na linguagem ABC, antecessora de Python @ramalhoorg
    • Trem:uma sequência de vagões>>> t = Trem(4)>>> len(t) >>> for vagao in t:4 ... print(vagao)>>> t[0] vagao #1vagao #1 vagao #2>>> t[3] vagao #3vagao #4 vagao #4>>> t[-1]vagao #4>>> t[4]Traceback (most recent call last): ...IndexError: vagao inexistente [4] @ramalhoorg
    • Protocolo de sequênciaclass Trem(object): def __init__(self, vagoes): self.vagoes = vagoes def __getitem__(self, pos): indice = pos if pos >= 0 else self.vagoes + pos if 0 <= indice < self.vagoes: # indice 2 -> vagao #3 return vagao #%s % (indice+1) else: raise IndexError(vagao inexistente [%s] % pos) @ramalhoorg
    • Protocolo de sequência >>> t = Trem(4) >>> t[0] vagao #1 >>> t[3] vagao #4 __getitem__ >>> t[-1] vagao #4 >>> for vagao in t: ... print(vagao) vagao #1 __getitem__ vagao #2 vagao #3 vagao #4 @ramalhoorg
    • Protocolo de sequência • protocolo é uma interface informal • pode se implementado parcialmenteclass Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __getitem__(self, pos): indice = pos if pos >= 0 else self.num_vagoes + pos if 0 <= indice < self.num_vagoes: # indice 2 -> vagao #3 return vagao #%s % (indice+1) else: raise IndexError(vagao inexistente [%s] % pos) @ramalhoorg
    • Sequence ABC • Abstract Base Classfrom collections import Sequenceclass Trem(Sequence): def __init__(self, vagoes): self.vagoes = vagoes def __len__(self): return self.vagoes def __getitem__(self, pos): indice = pos if pos >= 0 else self.vagoes + pos if 0 <= indice < self.vagoes: # indice 2 -> vagao #3 return vagao #%s % (indice+1) else: raise IndexError(vagao inexistente [%s] % pos) @ramalhoorg
    • Herança de Sequence>>> t = Trem(4)>>> vagao #2 in tTrue>>> vagao #5 in tFalse>>> for i in reversed(t): print i...vagao #4 from collections import Sequencevagao #3vagao #2 class Trem(Sequence):vagao #1>>> t.index(vagao #2) def __init__(self, vagoes):1 self.vagoes = vagoes>>> t.index(vagao #7)Traceback (most recent call last): def __len__(self): return self.vagoes ...ValueError @ramalhoorg def __getitem__(self, pos):
    • InterfaceIterable• Iterable provê um método __iter__• O método __iter__ devolve uma instância de Iterator• Você normalmente não chama __iter__, quem chama é o Python • mas se precisar, use iter(x) @ramalhoorg
    • InterfaceIterator• Iterable provê um método next Python 2 ou __next__ Python 3• next/__next__ devolve o próximo item• Você normalmente não chama __next__ • mas se precisar, use next(x) Python ≥ 2.6 @ramalhoorg
    • Iterator é...• um padrão de projeto Design Patterns Gamma, Helm, Johnson & Vlissides Addison-Wesley, ISBN 0-201-63361-2 @ramalhoorg
    • Head FirstDesign PatternsPosterOReilly,ISBN 0-596-10214-3 @ramalhoorg
    • O padrãoIterator permiteacessar os itensde uma coleçãosequencialmente,isolando o clienteda implementaçãoda coleção.Head FirstDesign PatternsPosterOReilly,ISBN 0-596-10214-3 @ramalhoorg
    • Trem class Trem(object): com def __init__(self, vagoes): self.vagoes = vagoes iterator def __iter__(self): return IteradorTrem(self.vagoes) class IteradorTrem(object): def __init__(self, vagoes): self.atual = 0 self.ultimo_vagao = vagoes - 1 def next(self): if self.atual <= self.ultimo_vagao:>>> t = Trem(4) self.atual += 1>>> for vagao in t: return vagao #%s % (self.atual)... print(vagao) else:vagao #1 raise StopIteration()vagao #2vagao #3vagao #4 @ramalhoorg
    • Trem class Trem(object): def __init__(self, vagoes): self.vagoes = vagoes com def __iter__(self): return IteradorTrem(self.vagoes) iterator class IteradorTrem(object): def __init__(self, vagoes): self.atual = 0 self.ultimo_vagao = vagoes - 1 def next(self): if self.atual <= self.ultimo_vagao: self.atual += 1 return vagao #%s % (self.atual) iter(t) else: raise StopIteration() • for vagao in t:>>> t = Trem(4)>>> for vagao in t: • invoca iter(t)... print(vagao)vagao #1 • devolve IteradorTremvagao #2vagao #3vagao #4 @ramalhoorg
    • Trem class Trem(object): def __init__(self, vagoes): self.vagoes = vagoes com def __iter__(self): return IteradorTrem(self.vagoes) iterator class IteradorTrem(object): def __init__(self, vagoes): self.atual = 0 self.ultimo_vagao = vagoes - 1 def next(self): if self.atual <= self.ultimo_vagao: self.atual += 1 return vagao #%s % (self.atual) next(it_trem) else: raise StopIteration() • for vagao in t:>>> t = Trem(4)>>> for vagao in t: • invoca iter(t)... print(vagao)vagao #1 • devolve IteradorTremvagao #2vagao #3 • invoca next(it_trem) até quevagao #4 ele levante StopIteration @ramalhoorg
    • Em Python, um iterável é...• Um objeto a partir do qual a função iter consegue obter um iterador.• A chamada iter(x): interface Iterable • invoca x.__iter__() para obter um iterador • ou, se x.__iter__ não existe: • fabrica um iterador que acessa os itens de x sequenciamente: x[0], x[1], x[2] etc. protocolo de sequência @ramalhoorg
    • Iteração em C (exemplo 2) #include <stdio.h> int main(int argc, char *argv[]) { int i; for(i = 0; i < argc; i++) printf("%d : %sn", i, argv[i]); return 0; } $ ./args2 alfa bravo charlie 0 : ./args2 1 : alfa 2 : bravo 3 : charlie @ramalhoorg
    • Iteração em Python (ex. 2) nãoimport sys Pythonico!for i in range(len(sys.argv)): print i, :, sys.argv[i] $ python args2.py alfa bravo charlie 0 : args2.py 1 : alfa 2 : bravo 3 : charlie @ramalhoorg
    • Iteração em Python (ex. 2)import sys Pythonico!for i, arg in enumerate(sys.argv): print i, :, arg $ python args2.py alfa bravo charlie 0 : args2.py 1 : alfa 2 : bravo 3 : charlie @ramalhoorg
    • Iteração em Python (ex. 2)import sys isso constroi um geradorfor i, arg in enumerate(sys.argv): print i, :, argo gerador produz uma o gerador é um iterável preguiçoso! tupla (indice, item) sob demanda $ python args2.py alfa bravo charlie a cada iteração 0 : args2.py 1 : alfa 2 : bravo 3 : charlie @ramalhoorg
    • Como >>> e = enumerate(Turing)funciona >>> e <enumerate object at 0x...> >>> next(e)enumerate (0, T) >>> next(e) constroi isso (1, u) um gerador >>> next(e) enumerate (2, r) constroi >>> next(e) um gerador (3, i) >>> next(e)o gerador produz uma (4, n) tupla (indice, item) >>> next(e) a cada next(e) (5, g) >>> next(e) Traceback (most recent...): ... StopIteration @ramalhoorg
    • Iterator x generator• Gerador é uma generalização do iterador• Por definição, um objeto iterador produz itens iterando sobre outro objeto (alguma coleção)• Um gerador é um iterável que produz itens sem necessariamente acessar uma coleção • ele pode iterar sobre outro objeto mas também pode gerar itens por contra própria, sem qualquer dependência externa (ex. Fibonacci) @ramalhoorg
    • Função >>> def gen_123(): ... yield 1geradora ... ... ... yield 2 yield 3 >>> for i in gen_123(): print(i) 1 2 3• Quaquer função >>> g = gen_123() >>> g que tenha a palavra <generator object gen_123 at ...> >>> next(g) reservada yield em 1 seu corpo é uma >>> next(g) 2 função geradora >>> next(g) 3 >>> next(g) Traceback (most recent call last): ... StopIteration @ramalhoorg
    • Objeto >>> def gen_123(): ... yield 1gerador ... ... ... yield 2 yield 3 >>> for i in gen_123(): print(i) 1• Quando invocada, a 2 3 >>> g = gen_123() função geradora >>> g devolve um <generator object gen_123 at ...> >>> next(g) objeto gerador 1 >>> next(g) 2 >>> next(g) 3 >>> next(g) Traceback (most recent call last): ... StopIteration @ramalhoorg
    • Objeto >>> def gen_123(): ... yield 1gerador ... ... ... yield 2 yield 3 >>> for i in gen_123(): print(i)• O objeto gerador é 1 2 um iterável, 3 implementa >>> g = gen_123() >>> g .next() Python 2 <generator object gen_123 at ...> >>> next(g) ou 1 .__next__() Python 3 >>> next(g) 2• Use next(gerador) >>> next(g) 3 >>> next(g) Python ≥ 2.6 Traceback (most recent call last): ... StopIteration @ramalhoorg
    • >>> def gen_ab():......... print(iniciando...) yield A print(agora vem B:) Como funciona... yield B... print(FIM.)...>>> for s in gen_ab(): print(s)iniciando...A • Invocar umaagora vem B:B função geradoraFIM. produz um>>> g = gen_ab()>>> g # doctest: +ELLIPSIS objeto gerador<generator object gen_ab at 0x...>>>> next(g)iniciando... • O corpo da função sóA começa a ser>>> next(g)agora vem B: executado quando seB>>> next(g) invoca nextTraceback (most recent call last):...StopIteration @ramalhoorg
    • >>> def gen_ab():......... print(iniciando...) yield A print(agora vem B:) Como funciona... yield B... print(FIM.)...>>> for s in gen_ab(): print(s)iniciando...Aagora vem B:B • Quando next(g) éFIM. invocado, o corpo da>>> g = gen_ab()>>> g # doctest: +ELLIPSIS função é executado<generator object gen_ab at 0x...>>>> next(g) só até o primeiroiniciando... yieldA>>> next(g)agora vem B:B>>> next(g)Traceback (most recent call last):...StopIteration @ramalhoorg
    • >>> def gen_ab():......... print(iniciando...) yield A print(agora vem B:) Como funciona... yield B... print(FIM.)...>>> for s in gen_ab(): print(s)iniciando...Aagora vem B:B • Invocando next(g)FIM. novamente, a>>> g = gen_ab()>>> g # doctest: +ELLIPSIS execução avança até<generator object gen_ab at 0x...>>>> next(g) o próximo yieldiniciando...A>>> next(g)agora vem B:B>>> next(g)Traceback (most recent call last):...StopIteration @ramalhoorg
    • Trem c/ função geradora class Trem(object): def __init__(self, vagoes): self.vagoes = vagoes def __iter__(self): for i in range(self.vagoes): iter(t) yield vagao #%s % (i+1) • for vagao in t:>>> t = Trem(4)>>> for vagao in t: • invoca iter(t)... print(vagao)vagao #1 • devolve geradorvagao #2vagao #3 • invoca next(gerador) até quevagao #4 ele levante StopIteration @ramalhoorg
    • Iterador clássico x gerador class Trem(object): def __init__(self, vagoes):class Trem(object): self.vagoes = vagoes def __init__(self, vagoes): def __iter__(self): self.vagoes = vagoes for i in range(self.vagoes): yield vagao #%s % (i+1) def __iter__(self): return IteradorTrem(self.vagoes) 1 classe,class IteradorTrem(object): 3 linhas de código def __init__(self, vagoes): self.atual = 0 self.ultimo_vagao = vagoes - 1 def next(self): if self.atual <= self.ultimo_vagao: self.atual += 1 2 classes, return vagao #%s % (self.atual) else: 12 linhas de código raise StopIteration()
    • Iterador clássico x gerador class Trem(object): def __init__(self, vagoes):class Trem(object): self.vagoes = vagoes def __init__(self, vagoes): def __iter__(self): self.vagoes = vagoes for i in range(self.vagoes): yield vagao #%s % (i+1) def __iter__(self): return IteradorTrem(self.vagoes)class IteradorTrem(object): O gerador def __init__(self, vagoes): administra self.atual = 0 self.ultimo_vagao = vagoes - 1 o contexto def next(self): para você if self.atual <= self.ultimo_vagao: self.atual += 1 return vagao #%s % (self.atual) else: raise StopIteration()
    • Expressão geradora(genexp) >>> g = (c for c in ABC) >>> for l in g: ... print l ... A B C >>> g = (c for c in ABC) >>> g <generator object <genexpr> at 0x10045a410> @ramalhoorg
    • Expressão >>> g = (c for c in ABC)geradora >>> for l in g: ... ... print l A B C• Quando avaliada, >>> g = (c for c in ABC) >>> g <generator object <genexpr> at devolve um 0x10045a410> objeto gerador >>> next(g) A >>> next(g) B >>> next(g) C >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration @ramalhoorg
    • Trem c/ expressão geradora class Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __iter__(self): return (vagao #%s % (i+1) for i in range(self.num_vagoes)) iter(t) • for vagao in t:>>> t = Trem(4)>>> for vagao in t: • invoca iter(t)... print(vagao)vagao #1 • devolve geradorvagao #2vagao #3 • invoca gerador.next() até quevagao #4 ele levante StopIteration @ramalhoorg
    • Função geradora x genexpclass Trem(object): def __init__(self, vagoes): self.vagoes = vagoes def __iter__(self): for i in range(self.vagoes): yield vagao #%s % (i+1)class Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __iter__(self): return (vagao #%s % (i+1) for i in range(self.num_vagoes)) @ramalhoorg
    • Construtores embutidosque consomem eproduzem iteráveis• dict • reversed• enumerate • set• frozenset • tuple• list @ramalhoorg
    • Módulo itertools• geradores (potencialmente) infinitos • count(), cycle(), repeat()• geradores que combinam vários iteráveis • chain(), tee(), izip(), imap(), product(), compress()...• geradores que selecionam ou agrupam itens: • compress(), dropwhile(), groupby(), ifilter(), islice()...• Iteradores que produzem combinações • product(), permutations(), combinations()... @ramalhoorg
    • Geradores em Python 3• Várias funções e métodos da biblioteca padrão que devolviam listas agora devolvem geradores: • dict.keys(), dict.items(), dict.values()... • range(...) • como xrange no Py 2 (mais que um gerador)• Quando precisar de uma lista, basta passar o gerador para o construtor de list: list(range(10)) @ramalhoorg
    • Exemplo prático comfunções geradoras• Funções geradoras para desacoplar laços de leitura e escrita em uma ferramenta para conversão de bases de dados semi-estruturadashttps://github.com/ramalho/isis2json @ramalhoorg
    • Laço principal escrevearquivo JSON @ramalhoorg
    • Um outro laço lê osregistros a converter @ramalhoorg
    • Implementação possível:o mesmo laço lê e grava @ramalhoorg
    • Mas e a lógica para leroutro formato? @ramalhoorg
    • Funçõesdo script • iterMstRecords* • iterIsoRecords* • writeJsonArray • main * funções geradoras @ramalhoorg
    • Função main:leitura dosargumentos @ramalhoorg
    • Função main: seleçãodo formato escolha dadepende geradora de leitura função dode entrada formato de entrada função geradora escolhida é passada como argumento @ramalhoorg
    • writeJsonArray:escreverregistrosem JSON @ramalhoorg
    • writeJsonArray:itera sobre umas dasfunções geradoras @ramalhoorg
    • iterIsoRecords:ler registros função geradora!de arquivoISO-2709 @ramalhoorg
    • iterIsoRecords cria um novo dict a cada iteração produz (yield) registro na forma de um dict @ramalhoorg
    • iterMstRecords: função geradora!ler registrosde arquivoISIS .MST @ramalhoorg
    • iterMstRecordsiterIsoRecords cria um novo dict a cada iteração produz (yield) registro na forma de um dict @ramalhoorg
    • Geradores na prática @ramalhoorg
    • Geradores na prática @ramalhoorg
    • Geradores na prática @ramalhoorg
    • Faltou apresentar...• Envio de dados para um gerador através do método .send() (em vez de .next()), e uso de yield como uma .send() não costuma expressão para obter o dado ser usado no contexto enviado de iteração mas em pipelines• Uso de funções geradoras como co-rotinas “Coroutines are not related to iteration” David Beazley @ramalhoorg
    • Faltou apresentar...• Envio de dados para um gerador através do método .send() (em vez de .next()), e uso de yield como uma .send() não costuma expressão para obter o dado ser usado no contexto enviado de iteração mas em pipelines• Uso de funções geradoras como co-rotinas “Co-rotinas não têm relação com iteração” David Beazley @ramalhoorg
    • Oficinas Turing:computação para programadores • Próximos lançamentos: • 1ª turma de Python para quem usa Django • 3ª turma de Objetos Pythonicos • 4ª turma de Python para quem sabe Python Para saber mais sobre estes cursos ONLINE escreva para: ramalho@turing.com.br Turing.com.br