Upload
luciano-ramalho
View
1.705
Download
5
Embed Size (px)
DESCRIPTION
versão compacta do curso Objetos Pythonicos de Oficinas Turing
Citation preview
Objetos PythonicosOrientação a objetos e padrões de projeto em Python
Luciano [email protected]
outubro/2012
compacto
Conceito: “objeto”• Um componente de software
que inclui dados (campos) e comportamentos (métodos)
• Em geral, os atributos são manipulados pelos métodos do próprio objeto (encapsulamento)
Figuras: bycicle (bicicleta), The Java Tutorialhttp://docs.oracle.com/javase/tutorial/java/concepts/object.html
Terminologiapythonica
• Objetos possuem atributos
• Os atributos de um objeto podem ser:
• métodos
• atributos de dados
Figuras: bycicle (bicicleta), The Java Tutorialhttp://docs.oracle.com/javase/tutorial/java/concepts/object.html
“campos”
funções associadas
Exemplo: um objeto dict>>> d = {'AM':'Manaus', 'PE':'Recife', 'PR': 'Curitiba'}>>> d.keys()['PR', 'AM', 'PE']>>> d.get('PE')'Recife'>>> d.pop('PR')'Curitiba'>>> d{'AM': 'Manaus', 'PE': 'Recife'}>>> len(d)2>>> d.__len__()2
• Métodos: keys, get, pop, __len__ etc.
Exemplo: um objeto dict
>>> dir(d)['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
• dir revela atributos de um dict
• atributos de dados e métodos
Exemplo: um objeto dict
>>> d{'AM': 'Manaus', 'PE': 'Recife'}>>> d['AM']'Manaus'>>> d.__getitem__('AM')'Manaus'>>> d['MG'] = 'Belo Horizonte'>>> d.__setitem__('RJ', 'Rio de Janeiro')>>> d{'MG': 'Belo Horizonte', 'AM': 'Manaus', 'RJ': 'Rio de Janeiro', 'PE': 'Recife'}
• Sobrecarga de operadores:
• [ ]: __getitem__, __setitem__
Exemplo: um objeto dict
>>> d.__class__<type 'dict'>>>> type(d)<type 'dict'> >>> print d.__doc__dict() -> new empty dictionary.dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs.dict(seq) -> new dictionary initialized as if via: d = {} for k, v in seq: d[k] = vdict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
• Atributos de dados: __class__, __doc__
Exemplo: outro dict>>> d = dict()>>> d.keys()[]>>> d.get('bla')>>> print d.get('bla')None>>> d.get('spam')>>> print d.get('spam')None>>> d.pop('ovos')Traceback (most recent call last): File "<stdin>", line 1, in <module>KeyError: 'ovos'>>> d{}>>> len(d)0>>> d.__len__()0
• d = dict() é o mesmo que d = {}
import Tkinter
relogio = Tkinter.Label()
relogio.pack()relogio['font'] = 'Helvetica 120 bold'relogio['text'] = strftime('%H:%M:%S')
from time import strftime
def tic(): relogio['text'] = strftime('%H:%M:%S') def tac(): relogio.after(100, tictac)
tac()relogio.mainloop()
Note: no Tkinter, atributos de dados são acessados via [ ]:__getitem__ __setitem__
Exercício: construir e controlar Tkinter.Label
Tudo é um objeto
• Não existem “tipos primitivos” como em Java
• desde Python 2.2, dezembro de 2001
>>> 5 + 38>>> 5 .__add__(3)8>>> type(5)<type 'int'>
Tipos embutidos
• Implementados em C, por eficiência
• Comportamentos fixos
• Texto: str, unicode
• Números: int, long, float, complex, bool
• Coleções: list, tuple, dict, set, frozenset
• etc.
Funções são objetos>>> def fatorial(n):... '''devolve n!'''... return 1 if n < 2 else n * fatorial(n-1)... >>> fatorial(5)120>>> fat = fatorial>>> fat<function fatorial at 0x1004b5f50>>>> fat(42)1405006117752879898543142606244511569936384000000000L>>> fatorial.__doc__'devolve n!'>>> fatorial.__name__'fatorial'>>> fatorial.__code__<code object fatorial at 0x1004b84e0, file "<stdin>", line 1>>>> fatorial.__code__.co_varnames('n',)
Funções são objetos>>> fatorial.__code__.co_code'|\x00\x00d\x01\x00j\x00\x00o\x05\x00\x01d\x02\x00S\x01|\x00\x00t\x00\x00|\x00\x00d\x02\x00\x18\x83\x01\x00\x14S'>>> from dis import dis>>> dis(fatorial.__code__.co_code) 0 LOAD_FAST 0 (0) 3 LOAD_CONST 1 (1) 6 COMPARE_OP 0 (<) 9 JUMP_IF_FALSE 5 (to 17) 12 POP_TOP 13 LOAD_CONST 2 (2) 16 RETURN_VALUE >> 17 POP_TOP 18 LOAD_FAST 0 (0) 21 LOAD_GLOBAL 0 (0) 24 LOAD_FAST 0 (0) 27 LOAD_CONST 2 (2) 30 BINARY_SUBTRACT 31 CALL_FUNCTION 1 34 BINARY_MULTIPLY 35 RETURN_VALUE >>>
Bytecode da função fatorial
Tipagem forte• O tipo de um objeto nunca muda
• Raramente Python faz conversão automática de tipos
>>> a = 10>>> b = '9'>>> a + bTraceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: unsupported operand type(s) for +: 'int' and 'str'>>> a + int(b)19>>> str(a) + b'109'>>> 77 * NoneTraceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'>>>
Tipagem dinâmica: variáveis não têm tipo>>> def dobro(x):... return x * 2... >>> dobro(7)14>>> dobro(7.1)14.2>>> dobro('bom')'bombom'>>> dobro([10, 20, 30])[10, 20, 30, 10, 20, 30]>>> dobro(None)Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in dobroTypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
Orientação a objetos:a origem• Linguagem Simula 1967
• Noruega: Ole-Johan Dahl e Kristen Nygaard
• objetos, classes, sub-classes
• métodos virtuais (funções associadas a objetos específicos somente em tempo de execução, e não na compilação)
Orientação a objetos:evolução• Smalltalk 1980
• EUA, Xerox PARC (Palo Alto Research Center): Alan Kay, Dan Ingalls, Adele Goldberg et. al.
• Terminologia:
• “Object oriented programming”
• “message passing” (invocação de métodos), “late binding” (métodos virtuais)
Smalltalk 1980
Squeak: Smalltalk livre
Duck typing• “Se voa como um pato, nada como um pato e
grasna como um pato, é um pato.”
• Tipagem dinâmica permite duck typing (tipagem pato) estilo de programação que evita verificar os tipos dos objetos, mas apenas seus métodos
• No exemplo anterior, a função dobro funciona com qualquer objeto x que consiga fazer x * 2
• x implementa o método __mult__(n), para n inteiro
Tipagem forte x fraca• Tipagem forte x fraca refere-se a
conversão automática de valores de tipos diferente.
• Linguagens de tipagem fraca são muito liberais na mistura entre tipos, e isso é uma fonte de bugs.
> 0 == '0'true> 0 == ''true> '0' == ''false
Veja alguns resultados estranhos obtidos com JavaScript, que tem tipagem fraca.
Em Python as três expressões acima geram TypeError, e as três últimas resultam False.
Python tem tipagem forte.
> 10 + '9''109'> 10 + '9' * 119> '10' + 9 * 1'109'
JavaScript(ECMAScript 5) em Node.js 0.6
Tipagem forte x fraca,dinâmica x estática• Tipagem forte x fraca refere-se a conversão
automática de valores de tipos diferentes
• Tipagem dinâmica x estática refere-se à declaração dos tipos das variáveis, parâmetros formais e valores devolvidos pelas funções
• Linguagens de tipagem estática exigem a declaração dos tipos, ou usam inferência de tipos para garantir que uma variável será associada a somente a valores de um tipo
Tipagem em linguagensSmalltalk dinâmica forte
Python dinâmica forteRuby dinâmica forte
C (K&R) estática fracaC (ANSI) estática forte
Java estática forteC# estática forte
JavaScript dinâmica fracaPHP dinâmica fraca }
combinaçãoperigosa:bugs sorrateiros
Conversões automáticas
• Python faz algumas (poucas) conversões automáticas entre tipos:
• Promoção de int para float
• Promoção de str para unicode
• assume o encoding padrão: ASCII por default>>> 6 * 7.042.0>>> 'Spam, ' + u'eggs'u'Spam, eggs'>>>
Tipos são classes• Para criar um novo tipo de objeto, crie uma classe
• A função type devolve a classe de um objeto
>>> class Mamifero(object):... pass... >>> kingkong = Mamifero()>>> type(kingkong)<class '__main__.Mamifero'>>>> ClasseDoKingKong = type(kingkong)>>> dumbo = ClasseDoKingKong()>>> dumbo<__main__.Mamifero object at 0x10045dcd0>>>> type(dumbo)<class '__main__.Mamifero'>
Conceito: “classe”• Uma categoria, ou tipo, de objeto
• Uma idéia abstrata, uma forma platônica
• Exemplo: classe “Cão”:
• Eu digo: “Ontem eu comprei um cão”
• Você não sabe exatamente qual cão, mas sabe:
• é um mamífero, quadrúpede, carnívoro
• pode ser domesticado (normalmente)
• cabe em um automóvel
Exemplar de cão:instância da classe Cao
>>> rex = Cao() instanciação
Classe Caoinstanciação
class Mamifero(object): """lição de casa: implementar"""
class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def __init__(self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def __str__(self): return self.nome def __repr__(self): return 'Cao({0!r})'.format(self.nome)
>>> rex = Cao('Rex')>>> rexCao('Rex')>>> print rexRex>>> rex.qt_patas4>>> rex.latir()Rex: Au!>>> rex.latir(2)Rex: Au! Au!>>> rex.nervoso = True>>> rex.latir(3)Rex: Au! Au! Au! Au! Au! Au!
oopy/exemplos/cao.py
Como atributos são acessados• Ao buscar o.a (atributo a do objeto o da classe C),
o interpretador Python faz o seguinte:
• 1) acessa atributo a da instancia o; caso não exista...
• 2) acessa atributo a da classe C de o (type(o) ou o.__class__); caso nao exista...
• 3) busca o atributo a nas superclasses de C, conforme a MRO (method resolution order)
Classe Cao em Pythonclass Mamifero(object): """lição de casa: implementar"""
class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def __init__(self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def __str__(self): return self.nome def __repr__(self): return 'Cao({0!r})'.format(self.nome)
• __init__ é o construtor, ou melhor, o inicializador
• self é o 1º parâmetro formal em todos os métodos de instância
oopy/exemplos/cao.py
Classe Caoclass Mamifero(object): """lição de casa: implementar"""
class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def __init__(self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def __str__(self): return self.nome def __repr__(self): return 'Cao({0!r})'.format(self.nome)
>>> rex = Cao('Rex')>>> rexCao('Rex')>>> print rexRex>>> rex.qt_patas4>>> rex.latir()Rex: Au!>>> rex.latir(2)Rex: Au! Au!
• na invocação do método, a instância é passada automaticamente na posição do self
invocação
oopy/exemplos/cao.py
Classe Cao em Pythonclass Mamifero(object): """lição de casa: implementar"""
class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def __init__(self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def __str__(self): return self.nome def __repr__(self): return 'Cao({0!r})'.format(self.nome)
• atributos de dados na classe funcionam como valores default para os atributos das instâncas
• atributos da instância só podem ser acessados via self
oopy/exemplos/cao.py
Mamifero: superclassede Cao UML
diagrama de classe
oopy/exemplos/cao.pyes
peci
aliz
ação
class Mamifero(object): """lição de casa: implementar"""
class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def __init__(self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def __str__(self): return self.nome def __repr__(self): return 'Cao({0!r})'.format(self.nome)
generalização
Subclasses de Cao
class Pequines(Cao): nervoso = True class Mastiff(Cao): def latir(self, vezes=1): # o mastiff não muda seu latido quando nervoso print self.nome + ':' + ' Wuff!' * vezes class SaoBernardo(Cao): def __init__(self, nome): Cao.__init__(self, nome) self.doses = 10 def servir(self): if self.doses == 0: raise ValueError('Acabou o conhaque!') self.doses -= 1 msg = '{0} serve o conhaque (restam {1} doses)' print msg.format(self.nome, self.doses)
Diz a lenda que o cão São Bernardo leva um pequeno barril de conhaque para resgatar viajantes perdidos na neve.
• Continuação de cao.py
Subclassesde Cao
class Pequines(Cao): nervoso = True class Mastiff(Cao): def latir(self, vezes=1): # o mastiff não muda seu latido quando nervoso print self.nome + ':' + ' Wuff!' * vezes class SaoBernardo(Cao): def __init__(self, nome): Cao.__init__(self, nome) self.doses = 10 def servir(self): if self.doses == 0: raise ValueError('Acabou o conhaque!') self.doses -= 1 msg = '{0} serve o conhaque (restam {1} doses)' print msg.format(self.nome, self.doses)
>>> sansao = SaoBernardo('Sansao')>>> sansao.servir()Sansao serve o conhaque (restam 9 doses)>>> sansao.doses = 1>>> sansao.servir()Sansao serve o conhaque (restam 0 doses)>>> sansao.servir()Traceback (most recent call last): ...ValueError: Acabou o conhaque!
Subclasses de Cao
class Pequines(Cao): nervoso = True class Mastiff(Cao): def latir(self, vezes=1): # o mastiff não muda seu latido quando nervoso print self.nome + ':' + ' Wuff!' * vezes class SaoBernardo(Cao): def __init__(self, nome): Cao.__init__(self, nome) self.doses = 10 def servir(self): if self.doses == 0: raise ValueError('Acabou o conhaque!') self.doses -= 1 msg = '{0} serve o conhaque (restam {1} doses)' print msg.format(self.nome, self.doses)
• Continuação de cao.py
Interface• Interface é um conceito essencial em OO
• não depende de uma palavra reservada
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. DOI=10.1145/74877.74924 http://doi.acm.org/10.1145/74877.74924
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.
Interfaces e protocolos• Em SmallTalk, as interfaces eram chamadas de
“protocolos”.
• Não há verificação de interfaces na linguagem, mas algumas IDEs (“browsers”) permitem agrupar os métodos por protocolo para facilitar a leitura
• Um protocolo é uma interface informal, não declarada porém implementada por métodos concretos
Interfaces em Python
• Conceitualmente, sempre existiram como protocolos
• 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++
Exemplo: tômbola
• Sortear um a um todos os itens de uma coleção finita, sem repetir
• A mesma lógica é usada em sistemas para gerenciar banners online
Interface da tômbola
• Carregar itens
• Misturar itens
• Sortear um item
• Indicar se há mais itens
Projeto da tômbola
• UML:diagrama de classe
TDD: Test Driven Design
• Metodologia de desenvolvimento iterativa na qual, para cada funcionalidade nova, um teste é criado antes do código a ser implementado
• Esta inversão ajuda o programador a desenvolver com disciplina apenas uma funcionalidade de cada vez, mantendo o foco no teste que precisa passar
• Cada iteração de teste/implementação deve ser pequena e simples: “baby steps” (passinhos de bebê)
Doctests• Um dos módulos para fazer testes automatizados
na biblioteca padrão de Python
• o outro módulo é o unittest, da família xUnit
• Doctests foram criados para testar exemplos embutidos na documentação
• Usaremos doctests para especificar exercícios
• Exemplo: $ python -m doctest cao.rst
oopy/exemplos/cao.rst
Coding Dojo
• Implementação da classe Tombola, com testes feitos em Doctest
Implementação da tômbola
• Python 2.2 a 2.7
# coding: utf-8
import random
class Tombola(object): itens = None
def carregar(self, itens): self.itens = list(itens)
def carregada(self): return bool(self.itens)
def misturar(self): random.shuffle(self.itens)
def sortear(self): return self.itens.pop()
@ramalhoorg
Iteração em C e Python#include <stdio.h>
int main(int argc, char *argv[]) { int i; for(i = 0; i < argc; i++) printf("%s\n", argv[i]); return 0;} import sys
for arg in sys.argv: print arg
@ramalhoorg
Iteração em Java
class Argumentos { public static void main(String[] args) { for (int i=0; i < args.length; i++) System.out.println(args[i]); }}
$ java Argumentos alfa bravo charliealfabravocharlie
@ramalhoorg
Iteração em Java ≥1.5
class Argumentos2 { public static void main(String[] args) { for (String arg : args) System.out.println(arg); }}
• Enhanced for (for melhorado)
$ java Argumentos2 alfa bravo charliealfabravocharlie
ano:2004
@ramalhoorg
Iteração em Java ≥1.5
class Argumentos2 { public static void main(String[] args) { for (String arg : args) System.out.println(arg); }}
ano:2004
• Enhanced for (for melhorado)
import sys
for arg in sys.argv: print arg
ano:1991
@ramalhoorg
Exemplos de iteração
• Iteração em Python não se limita a tipos primitivos
• Exemplos
• string
• arquivo
• Django QuerySet
• Baralho (em: “OO em Python sem Sotaque”)
https://slideshare.net/ramalho/
@ramalhoorg
List comprehensions• Expressões que consomem iteráveis e
produzem listas
>>> 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]
List comprehension
● Compreensão de lista ou abrangência de lista
● Exemplo: usar todos os elementos:
– L2 = [n*10 for n in L]
qualquer iterável
resultado: uma lista
@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
Em Python o comando for itera 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
Tipos iteráveis embutidos
• basestring
• str
• unicode
• dict
• file
• frozenset
• list
• set
• tuple
• xrange
@ramalhoorg
Funções embutidas que consomem iteráveis• all
• any
• filter
• iter
• len
• map
• max
• min
• reduce
• sorted
• sum
• zip
@ramalhoorg
Operações com iteráveis• Desempacotamento
de tupla
• em atribuições
• em chamadas de funções>>> def soma(a, b):... return a + b... >>> soma(1, 2)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)7
>>> a, b, c = 'XYZ'>>> a'X'>>> b'Y'>>> c'Z'>>> g = (n for n in [1, 2, 3])>>> a, b, c = g>>> a1>>> b2>>> c3
@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
Protocolo de sequência>>> t = Trem(4)>>> len(t)4>>> t[0]'vagao #1'>>> t[3]'vagao #4'>>> t[-1]'vagao #4'>>> for vagao in t:... print(vagao)vagao #1vagao #2vagao #3vagao #4
__len__
__getitem__
__getitem__
@ramalhoorg
class Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __len__(self): return self.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)
Protocolo de sequência
• implementação “informal” da interface
Tômbola como sequência
• Permitir contagem de itens
• Permitir acesso direto a cada item pela ordem em que foi colocado
Sim, este é um exemplo forçado... mas daqui a pouco melhora!
@ramalhoorg
from collections import Sequence
class Trem(Sequence): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __len__(self): return self.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)
Interface Sequence
• collections.Sequence
@ramalhoorg
Herança de Sequence
>>> t = Trem(4)>>> 'vagao #2' in tTrue>>> 'vagao #5' in tFalse>>> for i in reversed(t): print i... vagao #4vagao #3vagao #2vagao #1>>> t.index('vagao #2')1>>> t.index('vagao #7')Traceback (most recent call last): ...ValueError
from collections import Sequence
class Trem(Sequence): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __len__(self): return self.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
InterfaceIterable
• Iterable provê um método __iter__
• O método __iter__ devolve uma instância de Iterator
@ramalhoorg
Iterator é...
• um padrão de projeto
Design PatternsGamma, Helm, Johnson & VlissidesAddison-Wesley, ISBN 0-201-63361-2
@ramalhoorg
Head First Design Patterns PosterO'Reilly, ISBN 0-596-10214-3
@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 PosterO'Reilly, ISBN 0-596-10214-3
@ramalhoorg
• for vagao in t:
• invoca iter(t)
• devolve IteradorTrem
• invoca itrem.next() até que ele levante StopIteration
class Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __iter__(self): return IteradorTrem(self.num_vagoes)
class IteradorTrem(object): 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: raise StopIteration()
Tremcomiterator
>>> t = Trem(4)>>> for vagao in t:... print(vagao)vagao #1vagao #2vagao #3vagao #4
iter(t)
Tômbola iterável
• Permitir iteração sobre tômbola em um laço for, devolvendo os itens em uma ordem sorteada
Agora o exemplo faz mais sentido!
@ramalhoorg
Recapitulando: 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.
protocolo de sequência
interface Iterable
@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 : %s\n", i, argv[i]); return 0;}
$ ./args2 alfa bravo charlie0 : ./args21 : alfa2 : bravo3 : charlie
@ramalhoorg
Iteração em Python (ex. 2)
import sys
for i, arg in enumerate(sys.argv): print i, ':', arg
$ python args2.py alfa bravo charlie0 : args2.py1 : alfa2 : bravo3 : charlie
@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çãogeradora
• Quando invocada, devolve um objeto gerador
• O objeto gerador é um iterável
>>> def g123():... yield 1... yield 2... yield 3... >>> for i in g123(): print i... 123>>> g = g123()>>> g<generator object g123 at 0x10e385e10>>>> g.next()1>>> g.next()2>>> g.next()3>>> g.next()Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration
@ramalhoorg
• for vagao in t:
• invoca iter(t)
• devolve gerador
• invoca gerador.next() até que ele levante StopIteration
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)
Trem c/ função geradora
>>> t = Trem(4)>>> for vagao in t:... print(vagao)vagao #1vagao #2vagao #3vagao #4
iter(t)
class Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __iter__(self): return IteradorTrem(self.num_vagoes)
class IteradorTrem(object): 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: raise StopIteration()
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)
Funçãogeradora
Iteradorclássico
12 linhasde código
3 linhas
mesma funcionalidade e desempenho!
@ramalhoorg
Expressãogeradora
• Quando avaliada, devolve um objeto gerador
• O objeto gerador é um iterável
>>> g = (n for n in [1, 2, 3])>>> for i in g: print i... 123>>> g = (n for n in [1, 2, 3])>>> g<generator object <genexpr> at 0x109a4deb0>>>> g.next()1>>> g.next()2>>> g.next()3>>> g.next()Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration
@ramalhoorg
• for vagao in t:
• invoca iter(t)
• devolve gerador
• invoca gerador.next() até que ele levante StopIteration
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))
Trem c/ expressão geradora
>>> t = Trem(4)>>> for vagao in t:... print(vagao)vagao #1vagao #2vagao #3vagao #4
iter(t)
@ramalhoorg
Construtores embutidos que consomem e produzem iteráveis
• dict
• enumerate
• frozenset
• list
• reversed
• set
• tuple
@ramalhoorg
• 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()...
Módulo itertools
@ramalhoorg
Exemplo prático com funções geradoras
• 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
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ções do script
• iterMstRecords*
• iterIsoRecords*
•writeJsonArray
•main
* funções geradoras
@ramalhoorg
Função main:leitura dosargumentos
@ramalhoorg
Função main: seleção do formatode entrada
função geradora escolhida é passada como argumento
escolha da função geradora de leitura depende do formato de entrada
@ramalhoorg
writeJsonArray:escrever registrosem JSON
@ramalhoorg
writeJsonArray:itera sobre umas das funções geradoras
@ramalhoorg
iterIsoRecords:ler registros de arquivoISO-2709
função geradora!
@ramalhoorg
iterIsoRecords
produz (yield) registro na forma de um dict
cria um novo dict a cada iteração
@ramalhoorg
iterMstRecords:ler registrosde arquivoISIS .MST
função geradora!
@ramalhoorg
iterIsoRecords
produz (yield) registro na forma de um dict
cria um novo dict a cada iteração
iterMstRecords
@ramalhoorg
Geradores na prática
@ramalhoorg
Geradores na prática
@ramalhoorg
Geradores na prática
Turing.com.br
Oficinas Turing:computação para programadores
• Próximos lançamentos:
• 4ª turma de Python para quem sabe Python
• 3ª turma de Objetos Pythonicos
• 1ª turma de Aprenda Python com um Pythonista