Your SlideShare is downloading. ×
0
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Encapsulamento com Descritores em Python
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Encapsulamento com Descritores em Python

845

Published on

0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
845
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
31
Comments
0
Likes
5
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  1. Descritores de atributos em Pythonoutubro/2012Luciano Ramalholuciano@ramalho.org
  2. Ritmo desta palestra Recomendação: Turing.com.br manter os olhos abertos
  3. Pré-requistos da palestra• Para acompanhar os slides a seguir, é preciso saber como funciona o básico de orientação a objetos em Python. Especificamente: • contraste entre atributos de classe e de instância • herança de atributos de classe (métodos e campos) • atributos protegidos: como e quando usar • como e quando usar a função super Turing.com.br
  4. Roteiro da palestra • A partir de um cenário inicial, implementamos uma classe muito simples • A partir daí, evoluímos a implementação em seis etapas para controlar o acesso aos campos das instâncias, usando propriedades, descritores e finalmente uma metaclasse • Nos slides, as etapas são identificadas por números: ➊, ➋, ➌... Turing.com.br
  5. 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 Turing.com.br
  6. ➊ 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): Turing.com.br self.peso * self.preco return
  7. ➊ a classe produz instâncias classe instâncias Turing.com.br
  8. ➊ 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): Turing.com.br self.peso * self.preco return
  9. ➊ 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... Turing.com.br
  10. ➊ 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... “We found that customers could order a negative quantity of books! And we would credit their credit card with the price...” Jeff Bezos Jeff Bezos of Amazon: Birth of a Salesman Turing.com.br WSJ.com - http://j.mp/VZ5not
  11. ➊ 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 Jeff Bezos of Amazon: Birth of a Salesman Turing.com.br WSJ.com - http://j.mp/VZ5not
  12. ➋ validação com property>>> ervilha = ItemPedido(ervilha partida, .5, 7.95)>>> ervilha.descricao, ervilha.peso, ervilha.preco(ervilha partida, .5, 7.95)>>> ervilha.peso = -10 parece umaTraceback (most recent call last): ... violação deValueError: valor deve ser > 0 encapsulamentomas a lógica do negócioestá preservada:peso agora é uma property Turing.com.br
  13. ➋ implementar propertyclass 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) Turing.com.br
  14. ➋ implementar propertyclass 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): atributo return self.__peso protegido @peso.setter def peso(self, valor): if valor > 0: self.__peso = valor else: raise ValueError(valor deve ser > 0) Turing.com.br
  15. ➋ implementar propertyclass 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 no __init__ a property já @property def peso(self): está em uso return self.__peso @peso.setter def peso(self, valor): if valor > 0: self.__peso = valor else: raise ValueError(valor deve ser > 0) Turing.com.br
  16. ➋ implementar propertyclass 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 atributo @property protegido def peso(self): __peso só é return self.__peso acessado nos @peso.setter métodos da def peso(self, valor): if valor > 0: property self.__peso = valor else: raise ValueError(valor deve ser > 0) Turing.com.br
  17. ➋ implementar propertyclass ItemPedido(object): def __init__(self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco ese quisermos def subtotal(self): a mesma lógica return self.peso * self.preco para o preco? @property def peso(self): return self.__peso teremos que @peso.setter duplicar tudo def peso(self, valor): if valor > 0: isso? self.__peso = valor else: raise ValueError(valor deve ser > 0) Turing.com.br
  18. ➌ validação com descriptor peso e preco são atributos da classe a lógica fica em ItemPedido __get__ e __set__, podendo ser reutilizada Turing.com.br
  19. ➌ validação com descriptor instâncias classe Turing.com.br
  20. class Quantidade(object):➌ def __init__(self): prefixo = self.__class__.__name__ chave = id(self) self.nome_alvo = %s_%s % (prefixo, chave) def __get__(self, instance, owner): return getattr(instance, self.nome_alvo) def __set__(self, instance, value):implementação if value > 0:do descriptor 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): Turing.com.br return self.peso * self.preco
  21. ➌ uso do descriptor a classe ItemPedido tem duas instâncias de Quantidade associadas a ela Turing.com.br
  22. ➌ uso do descriptor a classeclass 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 Turing.com.br
  23. ➌ uso do descriptorclass 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 Turing.com.br
  24. ➌ implementar o descriptorclass Quantidade(object): def __init__(self): prefixo = self.__class__.__name__ chave = id(self) self.nome_alvo = %s_%s % (prefixo, chave) def __get__(self, instance, owner): return getattr(instance, self.nome_alvo) uma classe def __set__(self, instance, value): if value > 0: com método setattr(instance, self.nome_alvo, value) __get__ é um else: raise ValueError(valor deve ser > 0) descriptor Turing.com.br
  25. ➌ implementar o descriptorclass Quantidade(object): def __init__(self): prefixo = self.__class__.__name__ chave = id(self) self.nome_alvo = %s_%s % (prefixo, chave) 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 Turing.com.br ao preco ou ao peso)
  26. ➌ implementar o descriptorclass Quantidade(object): def __init__(self): prefixo = self.__class__.__name__ chave = id(self) self.nome_alvo = %s_%s % (prefixo, chave) 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: instance é a instância de ItemPedido que está raise ValueError(valor deve ser > 0) self é a instância sendo acessada do descritor (associada Turing.com.br ao preco ou ao peso)
  27. ➌ implementar o descriptorclass Quantidade(object): nome_alvo é def __init__(self): o nome do prefixo = self.__class__.__name__ atributo da chave = id(self) self.nome_alvo = %s_%s % (prefixo, chave) instância de def __get__(self, instance, owner): ItemPedido return getattr(instance, self.nome_alvo) que este def __set__(self, instance, value): descritor if value > 0: setattr(instance, self.nome_alvo, value) (self) else: controla raise ValueError(valor deve ser > 0) Turing.com.br
  28. ➌ implementar o descriptor __get__ e __set__ manipulam o atributo-alvo Turing.com.br no objeto ItemPedido
  29. ➌ implementar o descriptorclass Quantidade(object): def __init__(self): prefixo = self.__class__.__name__ chave = id(self) self.nome_alvo = %s_%s % (prefixo, chave) 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 Turing.com.br atributo-alvo na instância de ItemPedido
  30. ➌ inicialização do descritor quando um descritor é instanciado, o atributo aoclass ItemPedido(object): qual ele será vinculado peso = Quantidade() preco = Quantidade() ainda não existe! def __init__(self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco def subtotal(self): exemplo: o return self.peso * self.preco atributo preco só passa a existir após a atribuição Turing.com.br
  31. ➌ implementar o descriptorclass Quantidade(object): def __init__(self): prefixo = self.__class__.__name__ chave = id(self) self.nome_alvo = %s_%s % (prefixo, chave) def __get__(self, instance, owner): return getattr(instance, self.nome_alvo) def __set__(self, instance, value): if value > 0: temos que inventar um setattr(instance, self.nome_alvo, value) else: nome para o atributo-alvo raise ValueError(valor deve ser > 0) onde será armazenado o valor na instância de ItemPedido Turing.com.br
  32. ➌ implementar o descriptor>>> ervilha = ItemPedido(ervilha partida, .5, 3.95)>>> ervilha.descricao, ervilha.peso, ervilha.preco(ervilha partida, .5, 3.95)>>> dir(ervilha)[Quantidade_4299545872, Quantidade_4299546064,__class__, __delattr__, __dict__, __doc__,__format__, __getattribute__, __hash__,__init__, __module__, __new__, __reduce__,__reduce_ex__, __repr__, __setattr__,__sizeof__, __str__, __subclasshook__,__weakref__, descricao, peso, preco,subtotal] nesta implementação, os nomes dos atributos-alvo não são descritivos, Turing.com.br dificultando a depuração
  33. ➍ usar nomes descritivos ItemPedido.__new__ invoca «quantidade».set_nome Turing.com.br para redefinir o nome_alvo
  34. ➍ usar nomes descritivos ItemPedido.__new__ invoca «quantidade».set_nome «quantidade».set_nome Turing.com.br redefine o nome_alvo
  35. ➍ usar nomes descritivos ItemPedido.__new__class ItemPedido(object): peso = Quantidade() invoca preco = Quantidade() «quantidade».set_nome def __new__(cls, *args, **kwargs): for chave, atr in cls.__dict__.items(): if hasattr(atr, set_nome): atr.set_nome(__ + cls.__name__, chave) return super(ItemPedido, cls).__new__(cls, *args, **kwargs) def __init__(self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco def subtotal(self): return self.peso * self.preco Turing.com.br
  36. ➍ usar nomes descritivos «quantidade».set_nomeclass Quantidade(object): redefine o nome_alvo 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) Turing.com.br
  37. ➍ usar nomes descritivosclass ItemPedido(object): peso = Quantidade() preco = Quantidade() def __new__(cls, *args, **kwargs): for chave, atr in cls.__dict__.items(): if hasattr(atr, set_nome): atr.set_nome(__ + cls.__name__, chave) return super(ItemPedido, cls).__new__(cls, *args, **kwargs) def __init__(self, descricao, peso, preco): self.descricao = descricao self.peso = peso self.preco = preco Quando __init__ def subtotal(self): executa, o descritor return self.peso * self.preco já está configurado com um nome de atributo-alvo Turing.com.br descritivo
  38. ➍ usar nomes descritivosItemPedido.__new__ invoca «quantidade».set_nome Turing.com.br
  39. ➍ usar nomes descritivos «quantidade».set_nome Turing.com.br redefine o nome_alvo
  40. ➍ nomes descritivos>>> ervilha = ItemPedido(ervilha partida, .5, 3.95)>>> ervilha.descricao, ervilha.peso, ervilha.preco(ervilha partida, 0.5, 3.95)>>> dir(ervilha)[__ItemPedido_peso, __ItemPedido_preco,__class__, __delattr__, __dict__, __doc__,__format__, __getattribute__, __hash__,__init__, __module__, __new__, __reduce__,__reduce_ex__, __repr__, __setattr__,__sizeof__, __str__, __subclasshook__,__weakref__, descricao, peso, preco,subtotal] nesta implementação e nas próximas, os nomes dos atributos- alvo seguem a convenção de Turing.com.br atributos protegidos de Python
  41. ➍ funciona, mas custa caro • ItemPedido aciona __new__ para construir cada nova instância • Porém a associação dos descritores é com a classe ItemPedido: o nome do atributo-alvo nunca vai mudar, uma vez definido corretamente Turing.com.br
  42. ➍ funciona, mas custa caro • Isso significa que para cada nova instância de ItemPedido que é criada, «quantidade».set_nome é invocado duas vezes • Mas o nome do atributo-alvo não tem porque mudar na vida de uma «quantidade» Turing.com.br
  43. ➍ funciona, mas custa caro1500000.0 1427386 7.3 ×1125000.0 980708 750000.0 585394 375000.0 80004 0 versão 1 versão 2 versão 3 versão 4 Número de instâncias de ItemPedido criadas por segundo (MacBook Pro 2011, Intel Core i7) Turing.com.br
  44. ➎ como evitar trabalho inútil • ItemPedido.__new__ resolveu mas de modo ineficiente. • Cada «quantidade» deve receber o nome do seu atributo-alvo apenas uma vez, quando a própria classe ItemPedido for criada • Para isso precisamos de uma... Turing.com.br
  45. ➎ metaclasses criam classes! metaclasses são classes cujas instâncias são classes Turing.com.br
  46. ➎ metaclasses criam classes! metaclasses são classes cujas instâncias são classes ItemPedido é uma instância de type type é a metaclasse default em Python: a classe que normalmente constroi Turing.com.br outras classes
  47. ➎ nossa metaclasse antes depois Turing.com.br
  48. ➎ nossa metaclasse ModeloMeta é a metaclasse que vai construir a classe ItemPedido ModeloMeta.__init__ fará apenas uma vez o que antes era feito em ItemPedido.__new__ Turing.com.br a cada nova instância
  49. ➎ nossa metaclasseclass 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) Assim dizemos que aclass ItemPedido(object): __metaclass__ = ModeloMeta classe ItemPedido herda peso = Quantidade() de object, mas é uma preco = Quantidade() instância (construida por) def __init__(self, descricao, peso, preco): self.descricao = descricao ModeloMeta self.peso = peso self.preco = preco def subtotal(self): return self.peso * self.preco Turing.com.br
  50. ➎ nossa metaclasseclass 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) Este __init__ invocaclass ItemPedido(object): __metaclass__ = ModeloMeta «quantidade».set_nome, peso = Quantidade() para cada descritor, uma preco = Quantidade() vez só, na inicialização da def __init__(self, descricao, peso, preco): self.descricao = descricao classe ItemPedido self.peso = peso self.preco = preco def subtotal(self): return self.peso * self.preco Turing.com.br
  51. ➎ nossa metaclasse em ação Turing.com.br
  52. ➎ nossa metaclasse em ação Turing.com.br
  53. ➎ nossa metaclasse em ação + Turing.com.br
  54. ➎ desempenho melhor1500000.0 1427386 mesmo desempenho, + nomes amigáveis1125000.0 980708 750000.0 585394 585771 375000.0 80004 0 versão 1 versão 2 versão 3 versão 4 versão 5 Número de instâncias de ItemPedido criadas por segundo (MacBook Pro 2011, Intel Core i7) Turing.com.br
  55. ➏ simplicidade aparente Turing.com.br
  56. ➏ 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 Turing.com.br
  57. ➏ 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): Turing.com.br __metaclass__ = ModeloMeta
  58. ➏ esquema final + Turing.com.br
  59. ➏ esquema final Turing.com.br
  60. Oficinas Turing: computação para programadores• Próximos lançamentos: • 4ª turma de Python para quem sabe Python • 3ª turma de Objetos Pythonicos • 1ª turma de Aprenda Python com um Pythonista Turing.com.br

×