SlideShare uma empresa Scribd logo
Descritores de atributos em Python
outubro/2012




Luciano Ramalho
luciano@ramalho.org
Ritmo desta palestra




                 Recomendação:
 Turing.com.br
                 manter os olhos abertos
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
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
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
➊ 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
➊ a classe produz instâncias




 classe
                 instâncias


 Turing.com.br
➊ 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
➊ 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
➊ 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
➊ 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
➋ 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 uma
Traceback (most recent call last):
  ...                                       violação de
ValueError: valor deve ser > 0              encapsulamento


mas a lógica do negócio
está preservada:
peso agora é uma property

  Turing.com.br
➋ 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')
 Turing.com.br
➋ 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):           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
➋ 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
                                              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
➋ 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             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
➋ implementar property
class 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
➌ 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
➌ validação com descriptor
 instâncias




                      classe



 Turing.com.br
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
➌ uso do descriptor
                  a classe
                  ItemPedido
                  tem duas
                  instâncias de
                  Quantidade
                  associadas a ela




 Turing.com.br
➌ 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




  Turing.com.br
➌ 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




  Turing.com.br
➌ implementar o descriptor
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)
                                                       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
➌ implementar o descriptor
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):
        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)
➌ implementar o descriptor
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):
        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)
➌ implementar o descriptor
class 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
➌ implementar o descriptor




                 __get__ e __set__
                 manipulam o atributo-alvo
 Turing.com.br   no objeto ItemPedido
➌ implementar o descriptor
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):
        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
➌ inicialização do descritor
                                        quando um descritor é
                                        instanciado, o atributo ao
class 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
➌ implementar o descriptor
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):
        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
➌ 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
➍ usar nomes descritivos




                 ItemPedido.__new__ invoca
                 «quantidade».set_nome
 Turing.com.br   para redefinir o nome_alvo
➍ usar nomes descritivos
 ItemPedido.__new__ invoca «quantidade».set_nome




                          «quantidade».set_nome
 Turing.com.br            redefine o nome_alvo
➍ 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
➍ usar nomes descritivos
                                              «quantidade».set_nome
class 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
➍ usar nomes descritivos
class 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
➍ usar nomes descritivos
ItemPedido.__new__ invoca «quantidade».set_nome




 Turing.com.br
➍ usar nomes descritivos




                 «quantidade».set_nome
 Turing.com.br   redefine o nome_alvo
➍ 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
➍ 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
➍ 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
➍ funciona, mas custa caro
1500000.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
➎ 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
➎ metaclasses criam classes!
                  metaclasses são
                   classes cujas
                  instâncias são
                      classes




 Turing.com.br
➎ 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
➎ nossa metaclasse
         antes       depois




 Turing.com.br
➎ 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
➎ nossa metaclasse
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)

                                                  Assim dizemos que a
class 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
➎ nossa metaclasse
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)
                                                  Este __init__ invoca
class 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
➎ nossa metaclasse em ação




 Turing.com.br
➎ nossa metaclasse em ação




 Turing.com.br
➎ nossa metaclasse em ação
                     +




 Turing.com.br
➎ desempenho melhor
1500000.0   1427386
                        mesmo desempenho,
                         + nomes amigáveis
1125000.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
➏ simplicidade aparente




 Turing.com.br
➏ 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
➏ 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
➏ esquema final   +




 Turing.com.br
➏ esquema final




 Turing.com.br
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

Mais conteúdo relacionado

Mais procurados

Programação Orientação a Objetos - Herança
Programação Orientação a Objetos - HerançaProgramação Orientação a Objetos - Herança
Programação Orientação a Objetos - Herança
Daniel Brandão
 
Exercícios - Herança - Java
Exercícios - Herança - JavaExercícios - Herança - Java
Exercícios - Herança - Java
Arthur Emanuel
 
ICC - Aula 05 - Estrutura de controle, sequencial e condicional
ICC - Aula 05 - Estrutura de controle, sequencial e condicionalICC - Aula 05 - Estrutura de controle, sequencial e condicional
ICC - Aula 05 - Estrutura de controle, sequencial e condicional
Felipe J. R. Vieira
 
Algoritmos de busca
Algoritmos de buscaAlgoritmos de busca
Algoritmos de busca
Fernando Ferreira
 
Introdução ao MySQL
Introdução ao MySQLIntrodução ao MySQL
Introdução ao MySQL
Anderson Sanches
 
Python Programming Essentials - M8 - String Methods
Python Programming Essentials - M8 - String MethodsPython Programming Essentials - M8 - String Methods
Python Programming Essentials - M8 - String Methods
P3 InfoTech Solutions Pvt. Ltd.
 
Aula 5 - Estruturas de seleção simples e composta - parte 1
Aula 5 - Estruturas de seleção simples e composta - parte 1Aula 5 - Estruturas de seleção simples e composta - parte 1
Aula 5 - Estruturas de seleção simples e composta - parte 1
Pacc UAB
 
Estruturas de Dados - Tabelas de Espalhamento (Hash Table)
Estruturas de Dados - Tabelas de Espalhamento (Hash Table)Estruturas de Dados - Tabelas de Espalhamento (Hash Table)
Estruturas de Dados - Tabelas de Espalhamento (Hash Table)
Erick Petrucelli
 
Iteraveis e geradores em Python
Iteraveis e geradores em PythonIteraveis e geradores em Python
Iteraveis e geradores em Python
Luciano Ramalho
 
Algoritmos de ordenação
Algoritmos de ordenaçãoAlgoritmos de ordenação
Algoritmos de ordenação
Jonas Mendonça
 
Ordenação
OrdenaçãoOrdenação
Ordenação
Sérgio Souza Costa
 
Aula 6 - Estruturas de seleção encadeada - parte 1
Aula 6 - Estruturas de seleção encadeada - parte 1Aula 6 - Estruturas de seleção encadeada - parte 1
Aula 6 - Estruturas de seleção encadeada - parte 1
Pacc UAB
 
Java vetores e matrizes
Java   vetores e matrizesJava   vetores e matrizes
Java vetores e matrizes
Armando Daniel
 
Linguagem C - Vetores
Linguagem C - VetoresLinguagem C - Vetores
Linguagem C - Vetores
Elaine Cecília Gatto
 
Aula 13 - Matrizes
Aula 13 - MatrizesAula 13 - Matrizes
Aula 13 - Matrizes
Pacc UAB
 
Automatizando seus testes com robot framework
Automatizando seus testes com robot frameworkAutomatizando seus testes com robot framework
Automatizando seus testes com robot framework
Claudenir Freitas
 
Python - Dicionários
Python - DicionáriosPython - Dicionários
Python - Dicionários
Marcos Castro
 
Aula 1 - Introdução a POO
Aula 1 -  Introdução a POOAula 1 -  Introdução a POO
Aula 1 - Introdução a POO
Daniel Brandão
 
POO - 22 - Tratamento de Exceções em Java
POO - 22 - Tratamento de Exceções em JavaPOO - 22 - Tratamento de Exceções em Java
POO - 22 - Tratamento de Exceções em Java
Ludimila Monjardim Casagrande
 
10 Java Script - Exemplos práticos
10 Java Script - Exemplos práticos10 Java Script - Exemplos práticos
10 Java Script - Exemplos práticos
Centro Paula Souza
 

Mais procurados (20)

Programação Orientação a Objetos - Herança
Programação Orientação a Objetos - HerançaProgramação Orientação a Objetos - Herança
Programação Orientação a Objetos - Herança
 
Exercícios - Herança - Java
Exercícios - Herança - JavaExercícios - Herança - Java
Exercícios - Herança - Java
 
ICC - Aula 05 - Estrutura de controle, sequencial e condicional
ICC - Aula 05 - Estrutura de controle, sequencial e condicionalICC - Aula 05 - Estrutura de controle, sequencial e condicional
ICC - Aula 05 - Estrutura de controle, sequencial e condicional
 
Algoritmos de busca
Algoritmos de buscaAlgoritmos de busca
Algoritmos de busca
 
Introdução ao MySQL
Introdução ao MySQLIntrodução ao MySQL
Introdução ao MySQL
 
Python Programming Essentials - M8 - String Methods
Python Programming Essentials - M8 - String MethodsPython Programming Essentials - M8 - String Methods
Python Programming Essentials - M8 - String Methods
 
Aula 5 - Estruturas de seleção simples e composta - parte 1
Aula 5 - Estruturas de seleção simples e composta - parte 1Aula 5 - Estruturas de seleção simples e composta - parte 1
Aula 5 - Estruturas de seleção simples e composta - parte 1
 
Estruturas de Dados - Tabelas de Espalhamento (Hash Table)
Estruturas de Dados - Tabelas de Espalhamento (Hash Table)Estruturas de Dados - Tabelas de Espalhamento (Hash Table)
Estruturas de Dados - Tabelas de Espalhamento (Hash Table)
 
Iteraveis e geradores em Python
Iteraveis e geradores em PythonIteraveis e geradores em Python
Iteraveis e geradores em Python
 
Algoritmos de ordenação
Algoritmos de ordenaçãoAlgoritmos de ordenação
Algoritmos de ordenação
 
Ordenação
OrdenaçãoOrdenação
Ordenação
 
Aula 6 - Estruturas de seleção encadeada - parte 1
Aula 6 - Estruturas de seleção encadeada - parte 1Aula 6 - Estruturas de seleção encadeada - parte 1
Aula 6 - Estruturas de seleção encadeada - parte 1
 
Java vetores e matrizes
Java   vetores e matrizesJava   vetores e matrizes
Java vetores e matrizes
 
Linguagem C - Vetores
Linguagem C - VetoresLinguagem C - Vetores
Linguagem C - Vetores
 
Aula 13 - Matrizes
Aula 13 - MatrizesAula 13 - Matrizes
Aula 13 - Matrizes
 
Automatizando seus testes com robot framework
Automatizando seus testes com robot frameworkAutomatizando seus testes com robot framework
Automatizando seus testes com robot framework
 
Python - Dicionários
Python - DicionáriosPython - Dicionários
Python - Dicionários
 
Aula 1 - Introdução a POO
Aula 1 -  Introdução a POOAula 1 -  Introdução a POO
Aula 1 - Introdução a POO
 
POO - 22 - Tratamento de Exceções em Java
POO - 22 - Tratamento de Exceções em JavaPOO - 22 - Tratamento de Exceções em Java
POO - 22 - Tratamento de Exceções em Java
 
10 Java Script - Exemplos práticos
10 Java Script - Exemplos práticos10 Java Script - Exemplos práticos
10 Java Script - Exemplos práticos
 

Semelhante a Encapsulamento com Descritores em Python

Encapsulamento com descritores
Encapsulamento com descritoresEncapsulamento com descritores
Encapsulamento com descritores
Luciano Ramalho
 
Meta-programacao em python
Meta-programacao em pythonMeta-programacao em python
Meta-programacao em python
Tiago Albineli Motta
 
Programando em python classes
Programando em python   classesProgramando em python   classes
Programando em python classes
samuelthiago
 
Programando em python - Classes
Programando em python -  ClassesProgramando em python -  Classes
Programando em python - Classes
IFRN -campus Ipanguaçu
 
05 poo-ii
05   poo-ii05   poo-ii
05 poo-ii
Ialis Cavalcante
 
AULA 1 - Classes e Objetos com codigicação Java.ppt
AULA 1 - Classes e Objetos com codigicação Java.pptAULA 1 - Classes e Objetos com codigicação Java.ppt
AULA 1 - Classes e Objetos com codigicação Java.ppt
JoberthSilva
 
AULA 1 - Classes e Objetos.ppt
AULA 1 - Classes e Objetos.pptAULA 1 - Classes e Objetos.ppt
AULA 1 - Classes e Objetos.ppt
JoberthSilva
 
07 construtores e finalize
07   construtores e finalize07   construtores e finalize
07 construtores e finalize
Artur Todeschini
 
Pacotes e Encapsulamento
Pacotes e EncapsulamentoPacotes e Encapsulamento
Pacotes e Encapsulamento
Denis L Presciliano
 
Pacotes e Encapsulamento
Pacotes e EncapsulamentoPacotes e Encapsulamento
Pacotes e Encapsulamento
Denis L Presciliano
 
Refactoring
RefactoringRefactoring
Refactoring
Bruno Lui
 
Introdução à análise orientada a objetos parte 2
Introdução à análise orientada a objetos parte 2Introdução à análise orientada a objetos parte 2
Introdução à análise orientada a objetos parte 2
irenescotolo
 
Java - Visão geral e Exercícios
Java - Visão geral e ExercíciosJava - Visão geral e Exercícios
Java - Visão geral e Exercícios
Arthur Emanuel
 
Revisão Sobre Programação Orientada a Objetos com Java
Revisão Sobre Programação Orientada a Objetos com Java Revisão Sobre Programação Orientada a Objetos com Java
Revisão Sobre Programação Orientada a Objetos com Java
Mario Jorge Pereira
 

Semelhante a Encapsulamento com Descritores em Python (14)

Encapsulamento com descritores
Encapsulamento com descritoresEncapsulamento com descritores
Encapsulamento com descritores
 
Meta-programacao em python
Meta-programacao em pythonMeta-programacao em python
Meta-programacao em python
 
Programando em python classes
Programando em python   classesProgramando em python   classes
Programando em python classes
 
Programando em python - Classes
Programando em python -  ClassesProgramando em python -  Classes
Programando em python - Classes
 
05 poo-ii
05   poo-ii05   poo-ii
05 poo-ii
 
AULA 1 - Classes e Objetos com codigicação Java.ppt
AULA 1 - Classes e Objetos com codigicação Java.pptAULA 1 - Classes e Objetos com codigicação Java.ppt
AULA 1 - Classes e Objetos com codigicação Java.ppt
 
AULA 1 - Classes e Objetos.ppt
AULA 1 - Classes e Objetos.pptAULA 1 - Classes e Objetos.ppt
AULA 1 - Classes e Objetos.ppt
 
07 construtores e finalize
07   construtores e finalize07   construtores e finalize
07 construtores e finalize
 
Pacotes e Encapsulamento
Pacotes e EncapsulamentoPacotes e Encapsulamento
Pacotes e Encapsulamento
 
Pacotes e Encapsulamento
Pacotes e EncapsulamentoPacotes e Encapsulamento
Pacotes e Encapsulamento
 
Refactoring
RefactoringRefactoring
Refactoring
 
Introdução à análise orientada a objetos parte 2
Introdução à análise orientada a objetos parte 2Introdução à análise orientada a objetos parte 2
Introdução à análise orientada a objetos parte 2
 
Java - Visão geral e Exercícios
Java - Visão geral e ExercíciosJava - Visão geral e Exercícios
Java - Visão geral e Exercícios
 
Revisão Sobre Programação Orientada a Objetos com Java
Revisão Sobre Programação Orientada a Objetos com Java Revisão Sobre Programação Orientada a Objetos com Java
Revisão Sobre Programação Orientada a Objetos com Java
 

Mais de Luciano Ramalho

Wiki-wiki S/A
Wiki-wiki S/AWiki-wiki S/A
Wiki-wiki S/A
Luciano Ramalho
 
Mongodb: agregação
Mongodb: agregaçãoMongodb: agregação
Mongodb: agregação
Luciano Ramalho
 
Introdução a linguagem Python
Introdução a linguagem PythonIntrodução a linguagem Python
Introdução a linguagem Python
Luciano Ramalho
 
Iteráveis e geradores (versão RuPy)
Iteráveis e geradores (versão RuPy)Iteráveis e geradores (versão RuPy)
Iteráveis e geradores (versão RuPy)
Luciano Ramalho
 
Iteraveis e geradores
Iteraveis e geradoresIteraveis e geradores
Iteraveis e geradores
Luciano Ramalho
 
Arduino: hardware hacking & coding dojo
Arduino: hardware hacking & coding dojoArduino: hardware hacking & coding dojo
Arduino: hardware hacking & coding dojo
Luciano Ramalho
 
Dojo com Processing
Dojo com ProcessingDojo com Processing
Dojo com Processing
Luciano Ramalho
 
Dojo com Arduino
Dojo com ArduinoDojo com Arduino
Dojo com Arduino
Luciano Ramalho
 
Python: Iteraveis, geradores etc
Python: Iteraveis, geradores etcPython: Iteraveis, geradores etc
Python: Iteraveis, geradores etc
Luciano Ramalho
 
Open Library no Mongodb
Open Library no MongodbOpen Library no Mongodb
Open Library no Mongodb
Luciano Ramalho
 
Jython no JavaOne Latin America 2011
Jython no JavaOne Latin America 2011Jython no JavaOne Latin America 2011
Jython no JavaOne Latin America 2011
Luciano Ramalho
 
Python para quem sabe Python (aula 2)
Python para quem sabe Python (aula 2)Python para quem sabe Python (aula 2)
Python para quem sabe Python (aula 2)
Luciano Ramalho
 
OO em Python sem sotaque
OO em Python sem sotaqueOO em Python sem sotaque
OO em Python sem sotaque
Luciano Ramalho
 
Modelos ricos
Modelos ricosModelos ricos
Modelos ricos
Luciano Ramalho
 
Python, a arma secreta do Google
Python, a arma secreta do GooglePython, a arma secreta do Google
Python, a arma secreta do Google
Luciano Ramalho
 
Ensinando OO com Python
Ensinando OO com PythonEnsinando OO com Python
Ensinando OO com Python
Luciano Ramalho
 
Alex Martelli's Python Design Patterns
Alex Martelli's Python Design PatternsAlex Martelli's Python Design Patterns
Alex Martelli's Python Design Patterns
Luciano Ramalho
 
Dspace em 5 minutos
Dspace em 5 minutosDspace em 5 minutos
Dspace em 5 minutos
Luciano Ramalho
 
JavaScript agora é sério (TDC 2011)
JavaScript agora é sério (TDC 2011)JavaScript agora é sério (TDC 2011)
JavaScript agora é sério (TDC 2011)
Luciano Ramalho
 
JavaScript agora é sério (FISL 2011)
JavaScript agora é sério (FISL 2011)JavaScript agora é sério (FISL 2011)
JavaScript agora é sério (FISL 2011)
Luciano Ramalho
 

Mais de Luciano Ramalho (20)

Wiki-wiki S/A
Wiki-wiki S/AWiki-wiki S/A
Wiki-wiki S/A
 
Mongodb: agregação
Mongodb: agregaçãoMongodb: agregação
Mongodb: agregação
 
Introdução a linguagem Python
Introdução a linguagem PythonIntrodução a linguagem Python
Introdução a linguagem Python
 
Iteráveis e geradores (versão RuPy)
Iteráveis e geradores (versão RuPy)Iteráveis e geradores (versão RuPy)
Iteráveis e geradores (versão RuPy)
 
Iteraveis e geradores
Iteraveis e geradoresIteraveis e geradores
Iteraveis e geradores
 
Arduino: hardware hacking & coding dojo
Arduino: hardware hacking & coding dojoArduino: hardware hacking & coding dojo
Arduino: hardware hacking & coding dojo
 
Dojo com Processing
Dojo com ProcessingDojo com Processing
Dojo com Processing
 
Dojo com Arduino
Dojo com ArduinoDojo com Arduino
Dojo com Arduino
 
Python: Iteraveis, geradores etc
Python: Iteraveis, geradores etcPython: Iteraveis, geradores etc
Python: Iteraveis, geradores etc
 
Open Library no Mongodb
Open Library no MongodbOpen Library no Mongodb
Open Library no Mongodb
 
Jython no JavaOne Latin America 2011
Jython no JavaOne Latin America 2011Jython no JavaOne Latin America 2011
Jython no JavaOne Latin America 2011
 
Python para quem sabe Python (aula 2)
Python para quem sabe Python (aula 2)Python para quem sabe Python (aula 2)
Python para quem sabe Python (aula 2)
 
OO em Python sem sotaque
OO em Python sem sotaqueOO em Python sem sotaque
OO em Python sem sotaque
 
Modelos ricos
Modelos ricosModelos ricos
Modelos ricos
 
Python, a arma secreta do Google
Python, a arma secreta do GooglePython, a arma secreta do Google
Python, a arma secreta do Google
 
Ensinando OO com Python
Ensinando OO com PythonEnsinando OO com Python
Ensinando OO com Python
 
Alex Martelli's Python Design Patterns
Alex Martelli's Python Design PatternsAlex Martelli's Python Design Patterns
Alex Martelli's Python Design Patterns
 
Dspace em 5 minutos
Dspace em 5 minutosDspace em 5 minutos
Dspace em 5 minutos
 
JavaScript agora é sério (TDC 2011)
JavaScript agora é sério (TDC 2011)JavaScript agora é sério (TDC 2011)
JavaScript agora é sério (TDC 2011)
 
JavaScript agora é sério (FISL 2011)
JavaScript agora é sério (FISL 2011)JavaScript agora é sério (FISL 2011)
JavaScript agora é sério (FISL 2011)
 

Encapsulamento com Descritores em Python

  • 1. Descritores de atributos em Python outubro/2012 Luciano Ramalho luciano@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 uma Traceback (most recent call last): ... violação de ValueError: valor deve ser > 0 encapsulamento mas a lógica do negócio está preservada: peso agora é uma property Turing.com.br
  • 13. ➋ 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') Turing.com.br
  • 14. ➋ 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): 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 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 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 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 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 property class 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 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 Turing.com.br
  • 23. ➌ 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 Turing.com.br
  • 24. ➌ implementar o descriptor 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) 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 descriptor 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): 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 descriptor 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): 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 descriptor class 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 descriptor 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): 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 ao class 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 descriptor 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): 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_nome class 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 descritivos class 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 descritivos ItemPedido.__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 caro 1500000.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 metaclasse 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) Assim dizemos que a class 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 metaclasse 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) Este __init__ invoca class 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 melhor 1500000.0 1427386 mesmo desempenho, + nomes amigáveis 1125000.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