Produtividade e qualidade em Python através da metaprogramação

Documentos relacionados
Descritores de atributos em Python

Python para quem sabe Python

Luciano Ramalho dezembro/2012. Objetos Pythonicos. Orientação a objetos e padrões de projeto em Python

Luciano Ramalho setembro/2012. Objetos Pythonicos. Orientação a objetos e padrões de projeto em Python

Luciano Ramalho setembro/2012. Objetos Pythonicos. Orientação a objetos e padrões de projeto em Python

Curso de Python em 5 Horas

Python: Exceções, Iteradores e Geradores. Claudio Esperança

Luciano Ramalho setembro/2012. Objetos Pythonicos. Orientação a objetos e padrões de projeto em Python

Aplicações de Dicionários. Prof. Alberto Costa Neto Programação em Python

Tipo Abstrato de Dados (TAD) Algoritmos e Estruturas de Dados I Prof. Tiago Eugenio de Melo

Computação II (MAB 225)

Luciano Ramalho setembro/2012. Objetos Pythonicos. Orientação a objetos e padrões de projeto em Python

Computação 1 - Python Aula 10 - Teórica: Estrutura de Dados - Dicionário. João Carlos, Carla Delgado, Ana Luisa Duboc 1/ 18

Computadores e Programação Engª Biomédica Departamento de Física Faculdade de Ciências e Tecnologia da Universidade de Coimbra Ano Lectivo 2003/2004

Programação procedimental

Listas, conjuntos e dicionários

Funções. Prof. Alberto Costa Neto Programação em Python

Python para quem sabe Python

Python: Classes. Claudio Esperança

Padrão para a codificação em Python

Luciano Iteráveis, geradores & cia: o caminho pythonico

Dicionários. Prof. Alberto Costa Neto Programação em Python

Linguagem de Programação Orientada a Objeto Abstração - Encapsulamento

Computação 2. Aula 7 Teórica professor: Leonardo Carvalho

Computação 1 - Python Aula 6 - Teórica: Listas 1/ 28

Entrada e saída. Marco A L Barbosa malbarbo.pro.br. Departamento de Informática Universidade Estadual de Maringá

DURAÇÃO DA PROVA: 2 horas

Singleton e Adapter. Professor: Nazareno Andrade (baseado no material de Hyggo Almeida e Jacques Sauvé)

Algoritmos e estrutura de dados

Encapsulamento e Métodos (Construtores e Estáticos) João Paulo Q. dos Santos

C com introdução a OO

Aula 4 Encapsulamento e Relacionamento Cleverton Hentz

Orientação a Objetos parte 2 ENCAPSULAMENTO, CLASSES, HERANÇAS

VB.NET - Orientação a objetos : conceitos básicos em 10

Algoritmos e estrutura de dados

Computadores e Programação Engª Biomédica Departamento de Física Faculdade de Ciências e Tecnologia da Universidade de Coimbra Ano Lectivo 2005/2006

Programação com objectos

Introdução à Programação / Programação I

Introdução à Programação Aula 20 Definição de classes

Fundamentos da Programação

Introdução à Programação

Computação I - Python Aula 4 - Teórica: Variáveis e Atribuição, Strings

Programação I Aula 19 Aritmética com racionais Pedro Vasconcelos DCC/FCUP

INTRODUÇÃO A CLASSES E ORIENTAÇÃO A OBJETOS EM PYTHON. George Gomes Cabral

Computação II (MAB 225)

È um tipo estruturado homogêneo

Luciano Ramalho setembro/2012. Objetos Pythonicos. Orientação a objetos e padrões de projeto em Python

PROGRAMAÇÃO ORIENTADA A OBJETOS: OCULTAR INFORMAÇÕES E ENCAPSULAMENTO

Modificadores de acesso e atributos de classe

Engenharia de Software

Prova Final de Linguagens de Programação - DCC024B -

Classes e Objetos. Prof. Fernando V. Paulovich 9 de agosto de 2010

Aula passada. Aula passada... Sequências Funções puras e modificadores. Listas Tuplos

Computação 2. Aula 8 Teórica professor: Leonardo Carvalho

Introdução à Programação Aula 17 Deteção e correção de erros

CIÊNCIA DA COMPUTAÇÃO - LINGUAGEM DE PROGRAMAÇÃO II REVISÃO POO

Aula 5 POO 1 Encapsulamento. Profa. Elaine Faria UFU

MAB224 Programação de Computadores II. Prof. Franklin Marquezino Universidade Federal do Rio de Janeiro

Aula de hoje. Tipos de Dados e Variáveis. Constantes literais. Dados. Variáveis. Tipagem dinâmica. SCC Introdução à Programação para Engenharias

Computação 1 - Python Aula 4 - Teórica Variáveis e Atribuição, Strings. João Carlos, Carla Delgado, Ana Luisa Duboc 1/ 30

[Desenvolvimento OO com Java] Modificadores de acesso e atributos de classe

Implementando subprogramas

1/ 26. Computação 1 - Python Aula 1 - Prática: Primeiros Passos - Função

Linguagem de Programação Orientada a Objeto Polimorfismo, Classes Abstractas e Interfaces

Fundamentos da Programação

MCG126 Programação de Computadores II

Computação I - Python Aula 1 - Teórica: Manipulação de Strings, Tuplas e Listas

Prova Final de Linguagens de Programação - DCC024 - Sistemas de Informação

Algoritmos e Programação 2. Objetos e Classes em Java. Classes. Revisão. Definições de classes incluem (geralmente):

Objetivos do mini curso. Conhecer a linguagem. Noção de programação utilizando Python. Aprender o báscio.

Programação orientada a objetos: exemplo com frações (conclusão)

Computação 2. Aula 11 Teórica professor: Leonardo Carvalho

Computação I - Python Aula 1 - Prática: Primeiros Passos- Função

Laboratório de Programação 1 Aula 04

Implementação de subprogramas

Orientação a Objetos. Encapsulamento ArrayLists. Gil Eduardo de Andrade

Análise Semântica. Eduardo Ferreira dos Santos. Outubro, Ciência da Computação Centro Universitário de Brasília UniCEUB 1 / 40

Computação 1 - Python Aula 4 - Teórica: Variáveis e Atribuição, Strings 1/ 26

GRASP. Nazareno Andrade (baseado em Hyggo Almeida e Jacques Sauvé)

Sistemas de Informação e Bases de Dados 2012/2013. Modelo Relacional. Alberto Sardinha 2012 IST

Java Standard Edition (JSE)

Sabemos que a classe é responsável por definir a estrutura e o comportamento de seus objetos. Portanto, os objetos são instâncias das classes.

Computação II (MAB 225)

Testes de correção (de defeitos)

Computação 2. Aula 6 Teórica professor: Leonardo Carvalho

Python. Resumo e Exercícios P3

Padrão: Versionamento

Projeto de Linguagem. Linguagens de Programação

Java First-Tier: Aplicações. Herança: Simples Múltipla. Orientação a Objetos em Java (III) Problemas de Herança Múltipla.

Collections Framework

INF011 Padrões de Projeto. 21 Memento

TÍTULO: UM ESTUDO SOBRE METAPROGRAMAÇÃO: AS LINGUAGENS DE PROGRAMAÇÃO PYTHON E RUBY

Programação de Computadores

Java para Desktop. Programação Orientada à Objetos 2 JSE

Programação de Jogos em Python

Programação Orientada a Objetos

Computação II MAB 225. Brunno Goldstein.

UNIVERSIDADE FEDERAL DE MATO GROSSO DO SUL SISTEMAS DE INFORMAÇÃO - CÂMPUS DE COXIM FUNDAMENTOS EM ORIENTAÇÃO A OBJETOS

Transcrição:

Produtividade e qualidade 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 Classes and 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 class ItemPedido(object): def init (self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco def subtotal(self): return self.peso * self.preco o método inicializador é conhecido como dunder init

➊ 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() -79.5 Descobrimos que os clientes conseguiam encomendar uma quantidade negativa de livros E nós creditávamos o valor em seus cartões... Jeff Bezos isso vai dar problema na hora de cobrar... Jeff Bezos of Amazon: Birth of a Salesman WSJ.com - http://j.mp/vz5not

➊ a solução clássica class ItemPedido(object): def init (self, descricao, peso, preco): self.descricao = descricao self.set_peso(peso) self.preco = preco def subtotal(self): return self.get_peso() * self.preco def get_peso(self): return self. peso def set_peso(self, valor): if valor > 0: self. peso = valor else: raise ValueError('valor deve ser > 0') mudanças na interface atributo protegido

➊ porém, a API foi alterada >>> ervilha.peso Traceback (most recent call last):... AttributeError: 'ItemPedido' object has no attribute 'peso' Antes podíamos acessar o peso de um item escrevendo apenas item.peso, mas agora não... Isso quebra código das classes clientes Python oferece outro caminho...

➋ o segundo doctest O peso de um ``ItemPedido`` deve ser maior que zero:: >>> ervilha.peso = 0 Traceback (most recent call last):... ValueError: valor deve ser > 0 >>> ervilha.peso 10 peso não foi alterado parece uma violação de encapsulamento mas a lógica do negócio é preservada

➋ validação com property O peso de um ``ItemPedido`` deve ser maior que zero:: >>> ervilha.peso = 0 Traceback (most recent call last):... ValueError: valor deve ser > 0 >>> ervilha.peso 10 peso agora é uma property

➋ implementar property class ItemPedido(object): def init (self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco def subtotal(self): return self.peso * self.preco @property def peso(self): return self. peso atributo protegido @peso.setter def peso(self, valor): if valor > 0: self. peso = valor else: raise ValueError('valor deve ser > 0')

➋ implementar property class ItemPedido(object): def init (self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco def subtotal(self): return self.peso * self.preco @property def peso(self): return self. peso @peso.setter def peso(self, valor): if valor > 0: self. peso = valor else: raise ValueError('valor deve ser > 0') no init a property já está em uso

➋ implementar property class ItemPedido(object): def init (self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco def subtotal(self): return self.peso * self.preco @property def peso(self): return self. peso @peso.setter def peso(self, valor): if valor > 0: self. peso = valor else: raise ValueError('valor deve ser > 0') o atributo protegido peso só é acessado nos métodos da property

➋ implementar property class ItemPedido(object): def init (self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco def subtotal(self): return self.peso * self.preco @property def peso(self): return self. peso @peso.setter def peso(self, valor): if valor > 0: self. peso = valor else: raise ValueError('valor deve ser > 0') e se quisermos a mesma lógica para o preco? teremos que duplicar tudo isso?

➌ os atributos gerenciados (managed attributes) ItemPedido descricao peso {descriptor} preco {descriptor} init subtotal usaremos descritores para gerenciar o acesso aos atributos peso e preco, preservando a lógica de negócio

➌ validação com descriptor peso e preco são atributos da classe ItemPedido a lógica fica em get e set, podendo ser reutilizada

➌ implementação do descritor classe new-style 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') 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

➊ 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

➌ implementação do 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') 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 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 a classe ItemPedido tem duas instâncias de Quantidade associadas a ela

➌ uso do descriptor 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 cada instância da classe Quantidade controla um atributo de ItemPedido

➌ uso do descriptor 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 todos os acessos a peso e preco passam pelos descritores

➌ 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') uma classe com método get é um descriptor

➌ 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: 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) instance é a instância de ItemPedido que está sendo acessada

➌ 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') nome_alvo é o nome do atributo da instância de ItemPedido que este descritor (self) controla

➌ implementar o descriptor 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') get e set usam getattr e setattr para manipular o atributo-alvo na instância de ItemPedido

➌ descriptor implementation cada instância de descritor gerencia um atributo específico das instâncias de ItemPedido e precisa de um nome_alvo específico

➌ inicialização do descritor 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 quando um descritor é instanciado, o atributo ao qual ele será vinculado ainda não existe exemplo: o atributo preco só passa a existir após a atribuição

➌ 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') temos que gerar um nome para o atributo-alvo onde será armazenado o valor na instância de 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') cada instância de Quantidade precisa criar e usar um nome_alvo diferente

➌ implementar o descriptor >>> ervilha = ItemPedido('ervilha partida',.5, 3.95) >>> ervilha.descricao, ervilha.peso, ervilha.preco ('ervilha partida',.5, 3.95) >>> dir(ervilha) ['Quantidade_0', 'Quantidade_1', ' class ', ' delattr ', ' dict ', ' doc ', ' format ', ' getattribute ', ' hash ', ' init ', ' module ', ' new ', ' reduce ', ' reduce_ex ', ' repr ', ' setattr ', ' sizeof ', ' str ', ' subclasshook ', ' weakref ', 'descricao', 'peso', 'preco', 'subtotal'] Quantidade_0 e Quantidade_1 são os atributos-alvo

➌ os atributos alvo ItemPedido descricao Quantidade_0 Quantidade_1 init subtotal «peso» «preco» «get/set atributo alvo» «descriptor» Quantidade nome_alvo init get set Quantidade_0 armazena o valor de peso Quantidade_1 armazena o valor de preco

➌ os atributos gerenciados ItemPedido descricao peso {descriptor} preco {descriptor} init subtotal clientes da classe ItemPedido não precisam saber como peso e preco são gerenciados 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 Para fazer isso, precisamos descobrir o nome do atributo gerenciado (ex. peso) isso não é tão simples quanto parece pode ser que não valha a pena complicar mais

➌ o desafio quando cada descritor é instanciado, a 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 classe ItemPedido não existe, e nem os atributos gerenciados

➌ o desafio 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 por exemplo, o atributo peso só é criado depois que Quantidade() é instanciada

Próximos passos Se o descritor precisar saber o nome do atributo gerenciado (talvez para salvar o valor em um banco de dados, usando nomes de colunas descritivos, como faz o Django)...então você vai precisar controlar a construção da classe gerenciada com uma...

Acelerando...

➎ metaclasses criam classes metaclasses são classes cujas instâncias são classes

➏ simplicidade aparente

➏ o poder da abstração from modelo import Modelo, Quantidade class ItemPedido(Modelo): peso = Quantidade() preco = Quantidade() def init (self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco

➏ módulo modelo.py class Quantidade(object): def init (self): self.set_nome(self. class. name, id(self)) def set_nome(self, prefix, key): self.nome_alvo = '%s_%s' % (prefix, key) 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') class ModeloMeta(type): def init (cls, nome, bases, dic): super(modelometa, cls). init (nome, bases, dic) for chave, atr in dic.items(): if hasattr(atr, 'set_nome'): atr.set_nome(' ' + nome, chave) class Modelo(object): metaclass = ModeloMeta

➏ esquema final +

➏ o poder da abstração

Referências Raymond Hettinger s Descriptor HowTo Guide Alex Martelli s Python in a Nutshell, 2e. David Beazley s Python Essential Reference, 4 th edition (covers Python 2.6)