Metaprogramação em Python



    Para ajudar em nosso dia a dia




                   
Metaprogramação


    Programação:
    Dados → Código → Dados

    Metaprogramação:
    Programa → Código → Programa



                   
Metaprogramação


    ● Refletir
    ● Alterar


    ● Estender




                  
Metaprogramação em python

    ●   Tudo é objeto, tudo é atribuível
    ●   Method missing
    ●   Metaclasses
    ●   Builtins types




                               
Classe tradicional

    class Pessoa(object):

      def __init__(self, nome):
        self.nome = nome

      def andar(self, metros):
        print "%s andou %d metros" % 
             (self.nome, metros)

    eu = Pessoa("Tiago")
    eu.andar(10)

                              
Outra maneira de criar

    def construtor(self, nome):
      self.nome = nome

    def andar(self, metros):
      print "%s andou %d metros" %
      (self.nome, metros)

    Pessoa = type("Pessoa", (object,), {
       "__init__": construtor,
       "andar": andar
    })

                              
Reflexão

    print eu.__class__
    > <class '__main__.Pessoa'>
    print Pessoa.__class__
    > <type 'type'>
    print Pessoa.__bases__
    > (<type 'object'>,)
    print Pessoa.__dict__
    > {'andar': <function andar at 0x7f27b159e230>, … }
    print eu.__dict__
    > {'nome': 'Tiago'}


                             
Métodos na classe

    def cantar(self, musica):
      print u"%s cantou ♪ %s ♪" % 
              (self.nome, musica)
    Pessoa.cantar = cantar

    eu = Pessoa("Tiago")
    eu.cantar("Pra Sonhar")
    > Tiago cantou ♪ Pra Sonhar ♪

    ela = Pessoa("Zilah")
    ela.cantar("Felicidade")
 
    > Zilah cantou ♪ Felicidade ♪
                            
Métodos na instância

    def cantar(self, musica):
      print u"%s cantou ♪ %s ♪" % 
              (self.nome, musica)

    from types import MethodType
    eu.cantar = MethodType(cantar, eu)
    eu.cantar("Pra Sonhar")
    > Tiago cantou ♪ Pra Sonhar ♪

    ela.cantar("Felicidade")
    > AttributeError: 'Pessoa' object has no
 
      attribute 'cantar'    
Adicionando superclasses

    class CantorMixin():
       def cantar(self, musica):
         print u"%s cantou ♪ %s ♪" % (self.nome, musica)

    eu = Pessoa("Tiago")
    Pessoa.__bases__ += (CantorMixin,)
    eu.cantar("Pra Sonhar")




                             
Qual utilidade?



        Alguns exemplos




                
1- Stub para teste

class TestUser(TestCase):

     def test_friends_names(self):
       friends = [{'name': 'Bruno'},{'name':'Renan'}]
       Facebook.get_friends = lambda obj: friends
       returned = User().friends_names()
       assert returned == ['Bruno','Renan']

     def setUp(self):
       self.__get_friends = Facebook.get_friends

     def tearDown(self):
       Facebook.get_friends = self.__get_friends
                                  
2- Estender frameworks

class ExtendedFindersMixin():
   def cached(self):
     #...
   def paged(self, page=None, size=None):
     #...

Manager.__bases__ += (ExtendedFindersMixin, )
QuerySet.__bases__ += (ExtendedFindersMixin, )

#Usando:
User.objects.filter(age__gte=18).paged(1,10).cached()


                             
3- NullObjects sem Factory

class UserQuiz(object):

     def __init__(self, id):
       if not id:
           self.null_object()

     def null_object(self):
       self.answer = MethodType(lambda a: False, self)
       self.answer_list = MethodType(lambda a: [], self)

     def answer(self): #...
     def answer_list(self): #...

                                    
Metaprogramação em python

    ●   Tudo é objeto, tudo é atribuível
    ●   Method missing
    ●   Metaclasses
    ●   Builtins types




                               
Attribute missing
class Pessoa(object):

     def __init__(self, nome):
       self.nome = nome

     def __getattr__(self, attr):
       return "%s nao possui o atributo %s"
              % (self.nome, attr)

eu = Pessoa("Tiago")
print eu.nome → Tiago
print eu.idade → Tiago nao possui o atributo idade

                                  
Method missing
class Pessoa(object):

     def __getattr__(self, attr):
       def tentar(*args):
          print "%s tentou %s (%s)" % (self.nome, attr, args)
       return tentar

     def andar(self, metros):
       print "%s andou %s metros" % (self.nome, metros)

eu = Pessoa("Tiago")
eu.andar(10) →Tiago andou 10 metros
eu.falar("Blablabla") →Tiago tentou falar (('Blablabla',))
                                 
Operadores

class Pessoa(object):
   def multiplicar(self, v):
     return [ Pessoa(self.nome) for i in range(v) ]
   def soma(self, v):
     return Pessoa(self.nome[0:3]+v.nome[0:3])

     __mul__ = multiplicar
     __add__ = soma

print Pessoa("Smith") * 3 → ['Smith', 'Smith', 'Smith']
print (Pessoa("Tiago") + Pessoa("Zilah")).nome → "TiaZil”


                                
Qual utilidade?



        Alguns exemplos




                
Lazy load
class SemanticEntity(object):

     def __init__(self, uri):
       self.uri = uri
       self.attrs = None

     def __getattr__(self, attr):
       self.__lazy_load()
       val = self.attrs.get(attr)
       if self.__is_entity(val):
           return self.__class__(val)
       return val

     def __lazy_load(self):
       if self.attrs is None:
          self.__load()
                                         
Lazy load

eu = SemanticEntity("http://globo.com/tiago”)

print eu.nome → Tiago
print eu.apaixonado_por.nome → Zilah
print eu.apaixonado_por.uri → http://globo.com/zilah
print eu.apaixonado_por.apaixonado_por.nome → Tiago




                              
Conquistar uma namorada

    class Pessoa(object):

      def coracao(self, v):
        if v == 3:
           return "Zilah"

      __lt__ = coracao

    tiago = Pessoa()
    print tiago <3


                               
Metaprogramação em python

    ●   Tudo é objeto, tudo é atribuível
    ●   Method missing
    ●   Metaclasses
    ●   Builtins types




                               
O que é?


    Classe:
    → Fábica de instâncias

    Metaclasse:
    → Fábrica de classes



                      
Metaclasse... chata

class MetaClasseChata(type):

    def __new__(cls, name, bases, dct):
      print "Alocando memoria para %s" % name
      return type.__new__(cls, name, bases, dct)

    def __init__(cls, name, bases, dct):
      print "Criando classe %s" % name
      super(MetaClasseChata, cls).__init__(name,bases,dct)

    def metodo_da_classe(cls):
      print "Metodo da classe"

                              
Metaclasse... chata

class ClasseChata(object):
   __metaclass__ = MetaClasseChata

print ClasseChata.metodo_da_classe()
print type(ClasseChata)

----------------------------------------------------

Alocando memoria para ClasseChata
Criando classe ClasseChata
Metodo da classe
<class '__main__.MetaClasseChata'>

                                        
Um exemplo legal



               Selfless python
    Por João Sebastião de Oliveira Bueno
       http://metapython.blogspot.com




                       
Mas antes: Descriptors

    from random import random

    class NumeroAleatorioDescriptor(object):
       def __get__(self, instance, classe):
         return int(random()*100)

    class Roleta(object):
       valor = NumeroAleatorioDescriptor()

    print Roleta().valor


                                
Selfless python
class SelflessDescriptor(object):
   def __init__(self, func):
     self.func = func
   def __get__(self, instance, class_):
     new_globals = self.func.func_globals.copy()
     new_globals["self"] = instance
     new_func = FunctionType(self.func.func_code,
         new_globals, self.func.func_name,
         self.func.func_defaults,
         self.func.func_closure)
     return new_func

class Selfless(type):
   def __new__(cls, name, bases, dict_):
     for key, val in dict_.items():
        if isinstance(val, FunctionType):
            dict_[key] = SelflessDescriptor(val)
 
     return type(name, bases, dict_)  
Selfless python

    __metaclass__ = Selfless

    class A:
       def __init__(a, b):
         self.a = a
         self.b = b




                                
Metaprogramação em python

    ●   Tudo é objeto, tudo é atribuível
    ●   Method missing
    ●   Metaclasses
    ●   Builtins types




                               
Fuuuuuuuu

from datetime import datetime
datetime.now = lambda: None
TypeError: can't set attributes of built-in/extension
           type 'datetime.datetime'

set.get = lambda: None
TypeError: can't set attributes of built-in/extension type 'set'

list.index = lambda i: None
TypeError: can't set attributes of built-in/extension type 'list'

str.split = lambda: None
TypeError: can't set attributes of built-in/extension type 'str'

                                   
Datetime: Jeitinho brasileiro
import datetime as dt_module
from datetime import datetime

class TestArtist(TestCase):
   def test_should_find_news_by_date(self):
     today = datetime.today()
     dt_module.datetime = DateTimeFake(today)

    def get_by_date_fake(obj, date):
      self.used_date = date
      return []
    NewsRepository.get_by_date = get_by_date_fake

    Artist().news()
    assert self.used_date == today
                                 
O jeito é uma herancinha
com você
class BetterDict(dict):

    def __getattr__(self, attr):
      return self.__getitem__(attr)

    def __setattr__(self, attr, val):
      self.__setitem__(attr, val)

x = BetterDict({'a': 'A'})
x['b'] = 'B'
x.c = 'C'

print x.a, x.b, x.c → A B C
print x['a'], x['b'], x['c'] → A B C
                                        
O jeito é uma herancinha
com você
class BetterList(list):

    def subtraction(self, other):
      for i in other:
         if i in self:
             self.remove(i)
      return self

    __isub__ = subtraction


x = BetterList([1,2,3,4])
x -= [2, 4]
print x → [1, 3]
                                     
Outras coisas legais...

        … mas que não são metaprogramação

    ●   Decorators
    ●   With Statement
    ●   Multiprocessing




                           
Conclusão

    ●   Ruby
    ●   Python
    ●   Java




                  
Mantenha contato

timotta@gmail.com

@timotta

http://programandosemcafeina.blogspot.com




                      

Meta-programacao em python

  • 1.
    Metaprogramação em Python Para ajudar em nosso dia a dia    
  • 2.
    Metaprogramação Programação: Dados → Código → Dados Metaprogramação: Programa → Código → Programa    
  • 3.
    Metaprogramação ● Refletir ● Alterar ● Estender    
  • 4.
    Metaprogramação em python ● Tudo é objeto, tudo é atribuível ● Method missing ● Metaclasses ● Builtins types    
  • 5.
    Classe tradicional class Pessoa(object): def __init__(self, nome): self.nome = nome def andar(self, metros): print "%s andou %d metros" % (self.nome, metros) eu = Pessoa("Tiago") eu.andar(10)    
  • 6.
    Outra maneira decriar def construtor(self, nome): self.nome = nome def andar(self, metros): print "%s andou %d metros" % (self.nome, metros) Pessoa = type("Pessoa", (object,), { "__init__": construtor, "andar": andar })    
  • 7.
    Reflexão print eu.__class__ > <class '__main__.Pessoa'> print Pessoa.__class__ > <type 'type'> print Pessoa.__bases__ > (<type 'object'>,) print Pessoa.__dict__ > {'andar': <function andar at 0x7f27b159e230>, … } print eu.__dict__ > {'nome': 'Tiago'}    
  • 8.
    Métodos na classe def cantar(self, musica): print u"%s cantou ♪ %s ♪" % (self.nome, musica) Pessoa.cantar = cantar eu = Pessoa("Tiago") eu.cantar("Pra Sonhar") > Tiago cantou ♪ Pra Sonhar ♪ ela = Pessoa("Zilah") ela.cantar("Felicidade")   > Zilah cantou ♪ Felicidade ♪  
  • 9.
    Métodos na instância def cantar(self, musica): print u"%s cantou ♪ %s ♪" % (self.nome, musica) from types import MethodType eu.cantar = MethodType(cantar, eu) eu.cantar("Pra Sonhar") > Tiago cantou ♪ Pra Sonhar ♪ ela.cantar("Felicidade") > AttributeError: 'Pessoa' object has no   attribute 'cantar'  
  • 10.
    Adicionando superclasses class CantorMixin(): def cantar(self, musica): print u"%s cantou ♪ %s ♪" % (self.nome, musica) eu = Pessoa("Tiago") Pessoa.__bases__ += (CantorMixin,) eu.cantar("Pra Sonhar")    
  • 11.
    Qual utilidade? Alguns exemplos    
  • 12.
    1- Stub parateste class TestUser(TestCase): def test_friends_names(self): friends = [{'name': 'Bruno'},{'name':'Renan'}] Facebook.get_friends = lambda obj: friends returned = User().friends_names() assert returned == ['Bruno','Renan'] def setUp(self): self.__get_friends = Facebook.get_friends def tearDown(self): Facebook.get_friends = self.__get_friends    
  • 13.
    2- Estender frameworks classExtendedFindersMixin(): def cached(self): #... def paged(self, page=None, size=None): #... Manager.__bases__ += (ExtendedFindersMixin, ) QuerySet.__bases__ += (ExtendedFindersMixin, ) #Usando: User.objects.filter(age__gte=18).paged(1,10).cached()    
  • 14.
    3- NullObjects semFactory class UserQuiz(object): def __init__(self, id): if not id: self.null_object() def null_object(self): self.answer = MethodType(lambda a: False, self) self.answer_list = MethodType(lambda a: [], self) def answer(self): #... def answer_list(self): #...    
  • 15.
    Metaprogramação em python ● Tudo é objeto, tudo é atribuível ● Method missing ● Metaclasses ● Builtins types    
  • 16.
    Attribute missing class Pessoa(object): def __init__(self, nome): self.nome = nome def __getattr__(self, attr): return "%s nao possui o atributo %s" % (self.nome, attr) eu = Pessoa("Tiago") print eu.nome → Tiago print eu.idade → Tiago nao possui o atributo idade    
  • 17.
    Method missing class Pessoa(object): def __getattr__(self, attr): def tentar(*args): print "%s tentou %s (%s)" % (self.nome, attr, args) return tentar def andar(self, metros): print "%s andou %s metros" % (self.nome, metros) eu = Pessoa("Tiago") eu.andar(10) →Tiago andou 10 metros eu.falar("Blablabla") →Tiago tentou falar (('Blablabla',))    
  • 18.
    Operadores class Pessoa(object): def multiplicar(self, v): return [ Pessoa(self.nome) for i in range(v) ] def soma(self, v): return Pessoa(self.nome[0:3]+v.nome[0:3]) __mul__ = multiplicar __add__ = soma print Pessoa("Smith") * 3 → ['Smith', 'Smith', 'Smith'] print (Pessoa("Tiago") + Pessoa("Zilah")).nome → "TiaZil”    
  • 19.
    Qual utilidade? Alguns exemplos    
  • 20.
    Lazy load class SemanticEntity(object): def __init__(self, uri): self.uri = uri self.attrs = None def __getattr__(self, attr): self.__lazy_load() val = self.attrs.get(attr) if self.__is_entity(val): return self.__class__(val) return val def __lazy_load(self): if self.attrs is None: self.__load()    
  • 21.
    Lazy load eu =SemanticEntity("http://globo.com/tiago”) print eu.nome → Tiago print eu.apaixonado_por.nome → Zilah print eu.apaixonado_por.uri → http://globo.com/zilah print eu.apaixonado_por.apaixonado_por.nome → Tiago    
  • 22.
    Conquistar uma namorada class Pessoa(object): def coracao(self, v): if v == 3: return "Zilah" __lt__ = coracao tiago = Pessoa() print tiago <3    
  • 23.
    Metaprogramação em python ● Tudo é objeto, tudo é atribuível ● Method missing ● Metaclasses ● Builtins types    
  • 24.
    O que é? Classe: → Fábica de instâncias Metaclasse: → Fábrica de classes    
  • 25.
    Metaclasse... chata class MetaClasseChata(type): def __new__(cls, name, bases, dct): print "Alocando memoria para %s" % name return type.__new__(cls, name, bases, dct) def __init__(cls, name, bases, dct): print "Criando classe %s" % name super(MetaClasseChata, cls).__init__(name,bases,dct) def metodo_da_classe(cls): print "Metodo da classe"    
  • 26.
    Metaclasse... chata class ClasseChata(object): __metaclass__ = MetaClasseChata print ClasseChata.metodo_da_classe() print type(ClasseChata) ---------------------------------------------------- Alocando memoria para ClasseChata Criando classe ClasseChata Metodo da classe <class '__main__.MetaClasseChata'>    
  • 27.
    Um exemplo legal Selfless python Por João Sebastião de Oliveira Bueno http://metapython.blogspot.com    
  • 28.
    Mas antes: Descriptors from random import random class NumeroAleatorioDescriptor(object): def __get__(self, instance, classe): return int(random()*100) class Roleta(object): valor = NumeroAleatorioDescriptor() print Roleta().valor    
  • 29.
    Selfless python class SelflessDescriptor(object): def __init__(self, func): self.func = func def __get__(self, instance, class_): new_globals = self.func.func_globals.copy() new_globals["self"] = instance new_func = FunctionType(self.func.func_code, new_globals, self.func.func_name, self.func.func_defaults, self.func.func_closure) return new_func class Selfless(type): def __new__(cls, name, bases, dict_): for key, val in dict_.items(): if isinstance(val, FunctionType): dict_[key] = SelflessDescriptor(val)   return type(name, bases, dict_)  
  • 30.
    Selfless python __metaclass__ = Selfless class A: def __init__(a, b): self.a = a self.b = b    
  • 31.
    Metaprogramação em python ● Tudo é objeto, tudo é atribuível ● Method missing ● Metaclasses ● Builtins types    
  • 32.
    Fuuuuuuuu from datetime importdatetime datetime.now = lambda: None TypeError: can't set attributes of built-in/extension type 'datetime.datetime' set.get = lambda: None TypeError: can't set attributes of built-in/extension type 'set' list.index = lambda i: None TypeError: can't set attributes of built-in/extension type 'list' str.split = lambda: None TypeError: can't set attributes of built-in/extension type 'str'    
  • 33.
    Datetime: Jeitinho brasileiro importdatetime as dt_module from datetime import datetime class TestArtist(TestCase): def test_should_find_news_by_date(self): today = datetime.today() dt_module.datetime = DateTimeFake(today) def get_by_date_fake(obj, date): self.used_date = date return [] NewsRepository.get_by_date = get_by_date_fake Artist().news() assert self.used_date == today    
  • 34.
    O jeito éuma herancinha com você class BetterDict(dict): def __getattr__(self, attr): return self.__getitem__(attr) def __setattr__(self, attr, val): self.__setitem__(attr, val) x = BetterDict({'a': 'A'}) x['b'] = 'B' x.c = 'C' print x.a, x.b, x.c → A B C print x['a'], x['b'], x['c'] → A B C    
  • 35.
    O jeito éuma herancinha com você class BetterList(list): def subtraction(self, other): for i in other: if i in self: self.remove(i) return self __isub__ = subtraction x = BetterList([1,2,3,4]) x -= [2, 4] print x → [1, 3]    
  • 36.
    Outras coisas legais... … mas que não são metaprogramação ● Decorators ● With Statement ● Multiprocessing    
  • 37.
    Conclusão ● Ruby ● Python ● Java    
  • 38.