Luciano Ramalho
luciano@ramalho.org
@ramalhoorg
Iteráveis, geradores & cia:
a essência do Python
Luciano Ramalho
• Arquiteto de soluções Web desde 1994
• 1º diretor técnico do Brasil Online, na Abril S/A
• Muitos clientes e alguns empregos desde então,
quase sempre atuando na Web (server-side)
• Atualmente: Oficinas Turing (vários cursos) e
Academia Python em parceria com Globalcode
• Sócio do Garoa Hacker Clube
@ramalhoorg
Exemplos
• Laço for em Python iterando sobre:
• string
• arquivo
• Django QuerySet
• Baralho
@ramalhoorg
Contraste
• laço for em BASIC, Pascal, C
• muito bons para incrementar inteiros!
• “foreach” em JavaScript, Bash
• parecidos com o de Python, mas limitados aos
tipos primitivos da linguagem
• “foreach” em Java
• “enhanced for” no Java 5
@ramalhoorg
Em Python o comando for
itera sobre... “iteráveis”
• Literalmente:
• “desmontável” = que pode ser desmontado
• “iterável” = que pode ser iterado
• E como se faz um objeto iterável em Python?
• forma legada: protocolo de sequência
• forma moderna: interface Iterable
@ramalhoorg
(
Interface? Em Python?
• Interface é um conceito essencial em OO
• Em Smalltalk: “protocolo”
The interface provides a boundary
between the implementations of an
abstraction and its clients. It limits the
amount of implementation detail visible
to clients. It also specifies the functionality
that implementations must provide.
P. S. Canning, W. R. Cook, W. L. Hill, and W. G. Olthoff. 1989. Interfaces for strongly-typed object-
oriented programming. In Conference proceedings on Object-oriented programming systems,
languages and applications (OOPSLA '89). ACM, New York, NY, USA, 457-467. @ramalhoorg
DOI=10.1145/74877.74924 http://doi.acm.org/10.1145/74877.74924
(
Interface? Em Python?
• Interface é um conceito essencial em OO
• Em Smalltalk: “protocolo”
A interface fornece uma separação entre
a implementação de uma abstração e seus
clientes. Ela limita os detalhes de
implementação que os clientes podem
ver. Também especifica a funcionalidade
que as implementações devem prover.
P. S. Canning, W. R. Cook, W. L. Hill, and W. G. Olthoff. 1989. Interfaces for strongly-typed object-
oriented programming. In Conference proceedings on Object-oriented programming systems,
languages and applications (OOPSLA '89). ACM, New York, NY, USA, 457-467. @ramalhoorg
DOI=10.1145/74877.74924 http://doi.acm.org/10.1145/74877.74924
)
Interface em Python
• Conceitualmente, sempre existiram
• Não havia maneira formal de especificar
interfaces em Python até a versão 2.5
• usava-se termos como “uma sequência”
ou “a file-like object”
• Agora temos ABC (Abstract Base Class)
• com herança múltipla, como em C++
@ramalhoorg
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 fazendo x[0], x[1], x[2] etc.
@ramalhoorg
O padrão
Iterator permite
acessar os itens
de uma coleção
sequencialmente,
isolando o cliente
da implementação
da coleção.
Head First
Design Patterns
Poster
O'Reilly,
ISBN 0-596-10214-3
@ramalhoorg
Trem
class Trem(object):
def __init__(self, num_vagoes):
self.num_vagoes = num_vagoes
def __iter__(self):
com return IteradorTrem(self.num_vagoes)
class IteradorTrem(object):
iterator
def __init__(self, num_vagoes):
self.atual = 0
self.ultimo_vagao = num_vagoes - 1
def next(self):
if self.atual <= self.ultimo_vagao:
self.atual += 1
return 'vagao #%s' % (self.atual)
else:
iter(t) raise StopIteration()
• for vagao in t:
>>> t = Trem(4)
>>> for vagao in t:
• invoca iter(t)
... print(vagao)
vagao #1
• devolve IteradorTrem
vagao #2
vagao #3 • invoca itrem.next() até que
vagao #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 fazendo x[0], x[1], x[2] etc.
protocolo de sequência @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 objeto gerador produz itens de forma
independente
• ele pode iterar sobre outro objeto mas também
pode gerar itens por contra própria, sem
qualquer dependência externa
@ramalhoorg
Função >>> def g123():
geradora
... yield 1
... yield 2
... yield 3
...
>>> for i in g123(): print i
...
• Quando invocada,
1
2
3
devolve um >>> g = g123()
>>> g
objeto gerador <generator object g123 at 0x10e385e10>
• O objeto gerador
>>> g.next()
1
>>> g.next()
é um iterável 2
>>> g.next()
3
>>> g.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration @ramalhoorg
Trem c/ função geradora
class Trem(object):
def __init__(self, num_vagoes):
self.num_vagoes = num_vagoes
def __iter__(self):
for i in range(self.num_vagoes):
yield 'vagao #%s' % (i+1)
iter(t)
• for vagao in t:
>>> t = Trem(4)
>>> for vagao in t:
• invoca iter(t)
... print(vagao)
vagao #1
• devolve gerador
vagao #2
vagao #3 • invoca gerador.next() até que
vagao #4 ele levante StopIteration @ramalhoorg
class Trem(object):
Iterador def __init__(self, num_vagoes):
self.num_vagoes = num_vagoes
def __iter__(self):
clássico
return IteradorTrem(self.num_vagoes)
class IteradorTrem(object):
def __init__(self, num_vagoes):
12 linhas self.atual = 0
self.ultimo_vagao = num_vagoes - 1
de código def next(self):
if self.atual <= self.ultimo_vagao:
self.atual += 1
return 'vagao #%s' % (self.atual)
else:
raise StopIteration()
mesma funcionalidade!
Função class Trem(object):
def __init__(self, num_vagoes):
geradora self.num_vagoes = num_vagoes
def __iter__(self):
for i in range(self.num_vagoes):
yield 'vagao #%s' % (i+1)
3 linhas
List comprehension
List 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]
@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
Expressão
geradora >>> g = (n for n in [1, 2, 3])
>>> for i in g: print i
...
1
2
3
• Quando avaliada, >>> g = (n for n in [1, 2, 3])
>>> g
devolve um <generator object <genexpr> at
0x109a4deb0>
objeto gerador >>> g.next()
1
• O objeto gerador >>> g.next()
2
é um iterável >>> g.next()
3
>>> g.next()
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 gerador
vagao #2
vagao #3 • invoca gerador.next() até que
vagao #4 ele levante StopIteration @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) >>> c
3 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)
@ramalhoorg
7
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
Exemplo prático de
função geradora
• Funções geradoras para desacoplar laços de
leitura e escrita em uma ferramenta para
conversão de bases de dados semi-estruturadas
https://github.com/ramalho/isis2json
@ramalhoorg
Funções geradoras como
co-rotinas
• Segundo David Beazley, co-rotinas não tem a ver
com iteração, e sim com pipelines
• então esse tema nem deveria ser tratado nesta
palestra
• mas como são implementadas como funções
geradoras, merece ser mencionado
@ramalhoorg
Exemplo prático de
co-rotina
• Função geradora como co-rotina para preservar
o contexto de callback em programação
assíncrona com o framework Tornado
https://github.com/oturing/ppqsp/
.../async/assincrono_clo.py
.../async/assincrono_ge.py
@ramalhoorg
Luciano Ramalho
luciano@ramalho.org
@ramalhoorg
oficina online:
Objetos Pythonicos
POO e padrões de projeto como devem ser em Python
Pré-inscrição: pre-oopy.turing.com.br