Sei sulla pagina 1di 36

Luciano Ramalho

luciano@ramalho.org

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.
• 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
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
from collections import Seque
vagao #2
vagao #1
class Trem(Sequence):
>>> t.index('vagao #2')
def __init__(self, num_va
1
self.num_vagoes = num
>>> t.index('vagao #7')
def __len__(self):
Traceback (most recent call last):
return self.num_vagoe
...
def __getitem__(self, pos
ValueError @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
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
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 e desempenho!


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
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
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
>>> class C(object): (dois _ _ à esquerda,
... def __init__(self, idade):
... self.__idade = idade nenhum à direita)
...
>>> 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

>>> a = C() violação de


>>> a.x = 10 encapsulamento?
>>> print a.x
10
>>> a.x = -10
>>> print a.x
pergunte-me como!
0
Propriedade:
implementação
• Apenas para leitura, via decorator:
class C(object):
atributo def __init__(self, x):
self.__x = x
protegido @property
def x(self):
return self.__x
decorator
Propriedade:
implementação
• Leitura class C(object):
def __init__(self, x=0):
e escrita 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

Potrebbero piacerti anche