def

Propaganda
Luciano Ramalho
[email protected]
setembro/2012
Objetos Pythonicos
Orientação a objetos e padrões de projeto em Python
Aula 4
• Recapitulando iteráveis etc.
• Herança múltipla, MRO e super
• Propriedades
• Polimorfismo
Objetivos desta aula
• Rever iteradores, geradores etc.
• Corrigir exercício sobre herança
• Entender a MRO (method resolution order) e a
função super
• Apresentar encapsulamento, atributos protegidos e
propriedades
• Apresentar polimorfismo
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
Em Python, um iterável é...
• Um objeto a partir do qual a função iter
consegue obter um iterador.
interface Iterable
• 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
@ramalhoorg
Protocolo
de sequência
• implementação “informal” da interface
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:
@ramalhoorg
raise IndexError('vagao inexistente %s' % pos)
Interface
Sequence
• collections.Sequence
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:
@ramalhoorg
raise IndexError('vagao inexistente %s' % pos)
Herança de
Sequence
>>> t = Trem(4)
>>> 'vagao #2' in t
True
>>> 'vagao #5' in t
False
>>> for i in reversed(t): print i
...
vagao #4
vagao #3
vagao #2
vagao #1
>>> t.index('vagao #2')
1
>>> t.index('vagao #7')
Traceback (most recent call last):
...
ValueError
from collections import Seque
class Trem(Sequence):
def __init__(self, num_va
self.num_vagoes = num
def __len__(self):
return self.num_vagoe
def __getitem__(self, pos
@ramalhoorg
indice = pos if pos >
Interface
Iterable
• Iterable provê um método
__iter__
• O método __iter__ devolve
uma instância de Iterator
@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
com
iterator
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()
•
>>> t = Trem(4)
>>> for vagao in t:
...
print(vagao)
vagao #1
vagao #2
vagao #3
vagao #4
for vagao in t:
•
•
invoca iter(t)
•
devolve IteradorTrem
invoca itrem.next() até que
ele levante StopIteration @ramalhoorg
Trem c/ função geradora
iter(t)
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)
•
>>> t = Trem(4)
>>> for vagao in t:
...
print(vagao)
vagao #1
vagao #2
vagao #3
vagao #4
for vagao in t:
•
•
invoca iter(t)
•
devolve gerador
invoca gerador.next() até que
ele levante StopIteration @ramalhoorg
Iterador
clássico
12 linhas
de código
Função
geradora
3 linhas
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()
mesma funcionalidade e desempenho!
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/ expressão geradora
iter(t)
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))
•
>>> t = Trem(4)
>>> for vagao in t:
...
print(vagao)
vagao #1
vagao #2
vagao #3
vagao #4
for vagao in t:
•
•
invoca iter(t)
•
devolve gerador
invoca gerador.next() até que
ele levante StopIteration @ramalhoorg
Módulo itertools
demonstração...
• 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
Módulo itertools
demonstração...
• 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
Solução do
exercício 1.5
• A classe ContadorTotalizadorAmigavel
não precisa implementar qualquer método
• nem mesmo __init__
@ramalhoorg
MRO
• method resolution order
>>> Contador.__mro__
(<class '__main__.Contador'>,
<type 'object'>)
>>> ContadorAmigavel.__mro__
(<class '__main__.ContadorAmigavel'>,
<class '__main__.Contador'>,
<type 'object'>)
>>> ContadorTotalizadorAmigavel.__mro__
(<class '__main__.ContadorTotalizadorAmigavel'>,
<class '__main__.ContadorTotalizador'>,
<class '__main__.ContadorAmigavel'>,
<class '__main__.Contador'>,
<type 'object'>)
Solução alternativa
• Herança simples
@ramalhoorg
Solução alternativa
• Evitar o losango (diamond)
• Herança simples
@ramalhoorg
O losango não é
necessariamente ruim
• Em Python ele sempre está presente quando se
usa herança múltipla
• as classes comuns (new style) herdam de object
• Herança múltipla deve ser usada com moderação
• classes mixin são uma forma segura
• contribuem métodos e campos sem
sobrescrever outros atributos
@ramalhoorg
Invocar método de
superclasse
• A forma mais simples
class ContadorTotalizador(Contador):
def __init__(self):
Contador.__init__(self)
self.total = 0
def incluir(self, item):
Contador.incluir(self, item)
self.total += 1
@ramalhoorg
Invocar método de
superclasse
• A forma mais correta
• utiliza a MRO automaticamente
class ContadorTotalizador(Contador):
def __init__(self):
super(ContadorTotalizador, self).__init__()
self.total = 0
def incluir(self, item):
super(ContadorTotalizador, self).incluir(item)
self.total += 1
@ramalhoorg
Abuso de
getters/
setters
• Desnessário
e...
não
pythonico!
@ramalhoorg
Getters/setters
• Necessários quando se usa campos privados
mas é desejável oferecer acesso controlado
a esses campos (encapsulamento)
• para leitura: definir método getter
• para escrita: definir método setter
• Getters e setters que não implementam lógica são
questionáveis em geral e desnecessários em Python
@ramalhoorg
Atributos protegidos
Sintaxe:
__atributo
(dois _ _ à esquerda,
nenhum à direita)
>>> class C(object):
...
def __init__(self, idade):
...
self.__idade = idade
...
>>> o = C(20)
>>> o.__idade
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'idade'
>>> dir(o)
['_C__idade', '__class__', '__delattr__', '__dict__'...]
>>> o._C__idade
20
“name mangling”: desfiguração do nome
Controle de acesso a
atributos em Python
• Não existem modificadores de acesso
(private, protected etc.)
• Todos os atributos são públicos
• A convenção _x é para o programador
(o interpretador ignora)
• A sintaxe __x (dois _ _) tem o efeito de criar
atributos protegidos contra sobrescrita acidental
Atributos protegidos
• Filosofia dos atributos
protegidos em Python:
• Salvaguarda (safety)
e não segurança (security)
• Evita acesso acidental
• Não evita acesso intencional
Propriedades
• Atributos que podem ser acessados como se
fossem campos, mas acionam métodos de modo
transparente
• sintaxe de acesso: o.x
• e não o.x()
• Isso permite definir campos públicos inicialmente,
sem precisar definir getters e setters que não
fazem nada e depois implementar properties
(se necessário)
Propriedades
• Encapsulamento para quem precisa de
encapsulamento
>>>
>>>
>>>
10
>>>
>>>
0
a = C()
a.x = 10
print a.x
a.x = -10
print a.x
violação de
encapsulamento?
pergunte-me como!
Propriedade:
implementação
• Apenas para leitura, via decorator:
atributo
protegido
decorator
class C(object):
def __init__(self, x):
self.__x = x
@property
def x(self):
return self.__x
Propriedade:
implementação
• Leitura
e escrita
class C(object):
def __init__(self, x=0):
self.__x = x
@property
def x(self):
return self.__x
@x.setter
def x(self, valor):
if valor >= 0:
self.__x = valor
else:
self.__x = 0
Propriedade:
exemplo de uso
class ContadorTotalizador(Contador):
def __init__(self):
super(ContadorTotalizador, self).__init__()
self.__total = 0
def incluir(self, item):
super(ContadorTotalizador, self).incluir(item)
self.__total += 1
@property
def total(self):
return self.__total
Polimorfismo: definição
O conceito de “polimorfismo” significa que podemos
tratar instâncias de diferentes classes da mesma forma.
Assim, podemos enviar uma mensagem a um objeto
sem saber de antemão qual é o seu tipo, e o objeto
ainda assim fará “a coisa certa”, ao menos do ponto
de dele.
Scott Ambler
The Object Primer, 2nd ed. - p. 173
Exemplo de polimorfismo
• A classe Baralho como sequência
• Live-coding com monkey-patching
• programação ao vivo com modificação de classe
em tempo de execução
Download