luciano@ramalho.org
setembro/2012
Objetos Pythonicos
Orientação a objetos e padrões de projeto em Python
Aula 4
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
@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):
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):
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()
>>> 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...
https://github.com/ramalho/isis2json
@ramalhoorg
Módulo itertools demonstração...
• 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
@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
@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
@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