SlideShare una empresa de Scribd logo
1 de 60
Descargar para leer sin conexión
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

Más contenido relacionado

La actualidad más candente

Excel Avançado - Aulas
Excel Avançado - AulasExcel Avançado - Aulas
Excel Avançado - AulasGustavo Sousa
 
Python - Introdução Básica
Python - Introdução BásicaPython - Introdução Básica
Python - Introdução BásicaChristian Perone
 
Developing Complex Business Rules with Drools Integration
Developing Complex Business Rules with Drools IntegrationDeveloping Complex Business Rules with Drools Integration
Developing Complex Business Rules with Drools IntegrationBonitasoft
 
Python. Объектно-ориентированное программирование
Python. Объектно-ориентированное программирование Python. Объектно-ориентированное программирование
Python. Объектно-ориентированное программирование Theoretical mechanics department
 
Semana10-ordenacao-pesquisa-vetores
Semana10-ordenacao-pesquisa-vetoresSemana10-ordenacao-pesquisa-vetores
Semana10-ordenacao-pesquisa-vetoresPedro Valente
 
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
 
Modelos de banco de dados
Modelos de banco de dadosModelos de banco de dados
Modelos de banco de dadosEdgar Stuart
 
Estrutura de Dados e Algoritmos com Java #02-12: Vetores e Arrays
Estrutura de Dados e Algoritmos com Java #02-12: Vetores e ArraysEstrutura de Dados e Algoritmos com Java #02-12: Vetores e Arrays
Estrutura de Dados e Algoritmos com Java #02-12: Vetores e ArraysLoiane Groner
 
Banco de Dados II Aula 03 - Modelagem de Dados (Modelo Lógico)
Banco de Dados II Aula 03 - Modelagem de Dados (Modelo Lógico)Banco de Dados II Aula 03 - Modelagem de Dados (Modelo Lógico)
Banco de Dados II Aula 03 - Modelagem de Dados (Modelo Lógico)Leinylson Fontinele
 
Aula 03.1 - Operadores Relacionais e Lógicos
Aula 03.1 - Operadores Relacionais e LógicosAula 03.1 - Operadores Relacionais e Lógicos
Aula 03.1 - Operadores Relacionais e LógicosMessias Batista
 
Modelo entidade relacionamento
Modelo entidade relacionamentoModelo entidade relacionamento
Modelo entidade relacionamentoCarlos Melo
 
Orientação a objetos em Python (compacto)
Orientação a objetos em Python (compacto)Orientação a objetos em Python (compacto)
Orientação a objetos em Python (compacto)Luciano Ramalho
 
Informática Básica - Criação de Tabelas e Gráficos no Microsoft Word 2010
Informática Básica - Criação de Tabelas e Gráficos no Microsoft Word 2010Informática Básica - Criação de Tabelas e Gráficos no Microsoft Word 2010
Informática Básica - Criação de Tabelas e Gráficos no Microsoft Word 2010Joeldson Costa Damasceno
 

La actualidad más candente (20)

Excel Avançado - Aulas
Excel Avançado - AulasExcel Avançado - Aulas
Excel Avançado - Aulas
 
Python - Introdução Básica
Python - Introdução BásicaPython - Introdução Básica
Python - Introdução Básica
 
Apostila C ANSI
Apostila C ANSIApostila C ANSI
Apostila C ANSI
 
Variáveis e portugol
Variáveis e portugolVariáveis e portugol
Variáveis e portugol
 
Developing Complex Business Rules with Drools Integration
Developing Complex Business Rules with Drools IntegrationDeveloping Complex Business Rules with Drools Integration
Developing Complex Business Rules with Drools Integration
 
Python. Объектно-ориентированное программирование
Python. Объектно-ориентированное программирование Python. Объектно-ориентированное программирование
Python. Объектно-ориентированное программирование
 
Semana10-ordenacao-pesquisa-vetores
Semana10-ordenacao-pesquisa-vetoresSemana10-ordenacao-pesquisa-vetores
Semana10-ordenacao-pesquisa-vetores
 
IDE Falcon C++
IDE Falcon C++IDE Falcon C++
IDE Falcon C++
 
Estrutura de dados - Filas
Estrutura de dados - FilasEstrutura de dados - Filas
Estrutura de dados - Filas
 
Curso de Python e Django
Curso de Python e DjangoCurso de Python e Django
Curso de Python e Django
 
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)
 
Modelos de banco de dados
Modelos de banco de dadosModelos de banco de dados
Modelos de banco de dados
 
Estrutura de Dados e Algoritmos com Java #02-12: Vetores e Arrays
Estrutura de Dados e Algoritmos com Java #02-12: Vetores e ArraysEstrutura de Dados e Algoritmos com Java #02-12: Vetores e Arrays
Estrutura de Dados e Algoritmos com Java #02-12: Vetores e Arrays
 
Banco de Dados II Aula 03 - Modelagem de Dados (Modelo Lógico)
Banco de Dados II Aula 03 - Modelagem de Dados (Modelo Lógico)Banco de Dados II Aula 03 - Modelagem de Dados (Modelo Lógico)
Banco de Dados II Aula 03 - Modelagem de Dados (Modelo Lógico)
 
Aula 03.1 - Operadores Relacionais e Lógicos
Aula 03.1 - Operadores Relacionais e LógicosAula 03.1 - Operadores Relacionais e Lógicos
Aula 03.1 - Operadores Relacionais e Lógicos
 
Modelo entidade relacionamento
Modelo entidade relacionamentoModelo entidade relacionamento
Modelo entidade relacionamento
 
Estrutura de Dados - Listas Encadeadas
Estrutura de Dados - Listas EncadeadasEstrutura de Dados - Listas Encadeadas
Estrutura de Dados - Listas Encadeadas
 
Orientação a objetos em Python (compacto)
Orientação a objetos em Python (compacto)Orientação a objetos em Python (compacto)
Orientação a objetos em Python (compacto)
 
Informática Básica - Criação de Tabelas e Gráficos no Microsoft Word 2010
Informática Básica - Criação de Tabelas e Gráficos no Microsoft Word 2010Informática Básica - Criação de Tabelas e Gráficos no Microsoft Word 2010
Informática Básica - Criação de Tabelas e Gráficos no Microsoft Word 2010
 
Introduction to OOP in Python
Introduction to OOP in PythonIntroduction to OOP in Python
Introduction to OOP in Python
 

Similar a Controle de atributos em Python com propriedades e descritores

Encapsulamento com descritores
Encapsulamento com descritoresEncapsulamento com descritores
Encapsulamento com descritoresLuciano Ramalho
 
Programando em python classes
Programando em python   classesProgramando em python   classes
Programando em python classessamuelthiago
 
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.pptJoberthSilva
 
AULA 1 - Classes e Objetos.ppt
AULA 1 - Classes e Objetos.pptAULA 1 - Classes e Objetos.ppt
AULA 1 - Classes e Objetos.pptJoberthSilva
 
07 construtores e finalize
07   construtores e finalize07   construtores e finalize
07 construtores e finalizeArtur Todeschini
 
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 2irenescotolo
 
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íciosArthur 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
 

Similar a Controle de atributos em Python com propriedades e descritores (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
 

Más de Luciano Ramalho

Introdução a linguagem Python
Introdução a linguagem PythonIntrodução a linguagem Python
Introdução a linguagem PythonLuciano 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
 
Arduino: hardware hacking & coding dojo
Arduino: hardware hacking & coding dojoArduino: hardware hacking & coding dojo
Arduino: hardware hacking & coding dojoLuciano Ramalho
 
Iteraveis e geradores em Python
Iteraveis e geradores em PythonIteraveis e geradores em Python
Iteraveis e geradores em PythonLuciano Ramalho
 
Python: Iteraveis, geradores etc
Python: Iteraveis, geradores etcPython: Iteraveis, geradores etc
Python: Iteraveis, geradores etcLuciano Ramalho
 
Jython no JavaOne Latin America 2011
Jython no JavaOne Latin America 2011Jython no JavaOne Latin America 2011
Jython no JavaOne Latin America 2011Luciano 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 sotaqueLuciano Ramalho
 
Python, a arma secreta do Google
Python, a arma secreta do GooglePython, a arma secreta do Google
Python, a arma secreta do GoogleLuciano Ramalho
 
Alex Martelli's Python Design Patterns
Alex Martelli's Python Design PatternsAlex Martelli's Python Design Patterns
Alex Martelli's Python Design PatternsLuciano 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
 

Más 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
 
Iteraveis e geradores em Python
Iteraveis e geradores em PythonIteraveis e geradores em Python
Iteraveis e geradores em Python
 
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)
 

Controle de atributos em Python com propriedades e descritores

  • 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