Explorar E-books
Categorias
Explorar Audiolivros
Categorias
Explorar Revistas
Categorias
Explorar Documentos
Categorias
em Python através da
metaprogramação
ou
a “visão radical” na prática
Luciano Ramalho
ramalho@python.pro.br
@ramalhoorg
Fluent Python (O’Reilly)
• Early Release: out/2014
• First Edition: jul/2015
• ~ 700 páginas
• Data structures
• Functions as objects
• Control flow
• Metaprogramming
PythonPro: escola virtual
• Instrutores: Renzo Nuccitelli e Luciano Ramalho
• Na sua empresa ou online ao vivo com
Adobe Connect: http://python.pro.br
• Python Prático
• Python Birds
• Objetos Pythônicos
• Python para quem sabe Python
Simply Scheme: preface
Nosso objetivo
• Expandir o vocabulário com uma idéia
poderosa:
descritores de atributos
O cenário
• Comércio de alimentos a granel
• Um pedido tem vários itens
• Cada item tem descrição,
peso (kg), preço unitário (p/ kg)
e sub-total
➊
➊ o primeiro doctest
=======
Passo 1
=======
!
Um pedido de alimentos a granel é uma coleção de ``ItemPedido``.
Cada item possui campos para descrição, peso e preço::
!
>>> from granel import ItemPedido
>>> ervilha = ItemPedido('ervilha partida', 10, 3.95)
>>> ervilha.descricao, ervilha.peso, ervilha.preco
('ervilha partida', 10, 3.95)
!
Um método ``subtotal`` fornece o preço total de cada item::
!
>>> ervilha.subtotal()
39.5
➊ mais simples, impossível
o método
inicializador é
conhecido como
class ItemPedido(object):! “dunder init”
!
def __init__(self, descricao, peso, preco):!
self.descricao = descricao!
self.peso = peso!
self.preco = preco!
!
def subtotal(self):!
return self.peso * self.preco
➊ porém, simples demais
>>> ervilha = ItemPedido('ervilha partida', .5, 7.95)
>>> ervilha.descricao, ervilha.peso, ervilha.preco
('ervilha partida', .5, 7.95)
>>> ervilha.peso = -10
>>> ervilha.subtotal()
isso vai dar
-79.5 problema na
hora de cobrar...
“Descobrimos que os clientes
conseguiam encomendar uma
quantidade negativa de livros!
E nós creditávamos o valor em
seus cartões...” Jeff Bezos
peso e preco
são atributos
da classe a lógica fica em
ItemPedido __get__ e __set__,
podendo ser reutilizada
class Quantidade(object):!
➌
__contador = 0!
!
def __init__(self):!
prefixo = self.__class__.__name__!
chave = self.__class__.__contador!
self.nome_alvo = '%s_%s' % (prefixo, chave)!
self.__class__.__contador += 1!
!
def __get__(self, instance, owner):!
return getattr(instance, self.nome_alvo)!
!
implementação def __set__(self, instance, value):!
do descritor if value > 0:!
setattr(instance, self.nome_alvo, value)!
else:!
raise ValueError('valor deve ser > 0')!
!
!
classe class ItemPedido(object):!
new-style peso = Quantidade()!
preco = Quantidade()!
!
def __init__(self, descricao, peso, preco):!
self.descricao = descricao!
self.peso = peso!
self.preco = preco!
!
def subtotal(self):!
return self.peso * self.preco
➊ a classe produz instâncias
classe
instâncias
➌ a classe descriptor
instâncias
classe
➌ uso do descriptor
a classe
ItemPedido
tem duas
instâncias de
Quantidade
associadas a ela
class Quantidade(object):!
➌
__contador = 0!
!
def __init__(self):!
prefixo = self.__class__.__name__!
chave = self.__class__.__contador!
self.nome_alvo = '%s_%s' % (prefixo, chave)!
self.__class__.__contador += 1!
!
def __get__(self, instance, owner):!
return getattr(instance, self.nome_alvo)!
!
implementação def __set__(self, instance, value):!
do descriptor if value > 0:!
setattr(instance, self.nome_alvo, value)!
else:!
raise ValueError('valor deve ser > 0')!
!
!
class ItemPedido(object):!
peso = Quantidade()!
preco = Quantidade()!
!
def __init__(self, descricao, peso, preco):!
self.descricao = descricao!
self.peso = peso!
self.preco = preco!
!
def subtotal(self):!
return self.peso * self.preco
➌ uso do descriptor
a classe
class ItemPedido(object):!
peso = Quantidade()!
ItemPedido
preco = Quantidade()! tem duas
!
def __init__(self, descricao, peso, preco):!
instâncias de
self.descricao = descricao! Quantidade
self.peso = peso!
self.preco = preco!
associadas a ela
!
def subtotal(self):!
return self.peso * self.preco
➌ uso do descriptor
class ItemPedido(object):!
peso = Quantidade()! cada instância
preco = Quantidade()!
! da classe
def __init__(self, descricao, peso, preco):! Quantidade
self.descricao = descricao!
self.peso = peso! controla um
!
self.preco = preco! atributo de
def subtotal(self):! ItemPedido
return self.peso * self.preco
➌ uso do descriptor
todos os acessos
a peso e preco
class ItemPedido(object):! passam pelos
peso = Quantidade()!
preco = Quantidade()! descritores
!
def __init__(self, descricao, peso, preco):!
self.descricao = descricao!
self.peso = peso!
self.preco = preco!
!
def subtotal(self):!
return self.peso * self.preco
➌ implementar o descriptor
class Quantidade(object):!
__contador = 0!
!
def __init__(self):!
prefixo = self.__class__.__name__!
chave = self.__class__.__contador!
self.nome_alvo = '%s_%s' % (prefixo, chave)!
self.__class__.__contador += 1!
!
def __get__(self, instance, owner):!
return getattr(instance, self.nome_alvo)! uma classe
!
def __set__(self, instance, value):! com método
if value > 0:!
setattr(instance, self.nome_alvo, value)!
__get__ é um
else:! descriptor
raise ValueError('valor deve ser > 0')
➌ implementar o descriptor
class Quantidade(object):!
__contador = 0!
!
def __init__(self):!
prefixo = self.__class__.__name__!
chave = self.__class__.__contador!
self.nome_alvo = '%s_%s' % (prefixo, chave)!
self.__class__.__contador += 1!
!
def __get__(self, instance, owner):!
return getattr(instance, self.nome_alvo)!
!
def __set__(self, instance, value):!
if value > 0:!
setattr(instance, self.nome_alvo, value)!
else:!
raise ValueError('valor deve ser > 0')
self é a instância
do descritor (associada
ao preco ou ao peso)
➌ implementar o descriptor
class Quantidade(object):!
__contador = 0!
!
def __init__(self):!
prefixo = self.__class__.__name__!
chave = self.__class__.__contador!
self.nome_alvo = '%s_%s' % (prefixo, chave)!
self.__class__.__contador += 1!
!
def __get__(self, instance, owner):!
return getattr(instance, self.nome_alvo)!
!
def __set__(self, instance, value):!
if value > 0:!
instance é a instância
setattr(instance, self.nome_alvo, value)!
else:!
de ItemPedido que está
raise ValueError('valor deve ser > 0')
__get__ e __set__
manipulam o atributo-alvo
no objeto ItemPedido
➌ implementar o descriptor
class Quantidade(object):!
__contador = 0!
!
def __init__(self):!
prefixo = self.__class__.__name__!
chave = self.__class__.__contador!
self.nome_alvo = '%s_%s' % (prefixo, chave)!
self.__class__.__contador += 1!
!
def __get__(self, instance, owner):!
return getattr(instance, self.nome_alvo)!
!
def __set__(self, instance, value):!
if value > 0:!
setattr(instance, self.nome_alvo, value)!
else:!
raise ValueError('valor deve ser > 0')
clientes da classe
ItemPedido ItemPedido não precisam
descricao
saber como peso e preco
peso {descriptor}
preco {descriptor}
são gerenciados
__init__
subtotal
E nem precisam saber que
Quantidade_0 e
Quantidade_1 existem!
➌ próximos passos
• Seria melhor se os atributos-alvo fossem
atributos protegidos
• _ItemPedido__peso em vez de
_Quantitade_0
• Alex Martelli’s
Python in a Nutshell, 2e.
• David Beazley’s
Python Essential Reference,
4th edition
(covers Python 2.6)