Tecnologia

Metaprogramação em Python

Por: , abril 9, 2015

Python é uma linguagem que possui ferramentas bastante poderosas e que, infelizmente, não são muitas pessoas que às utilizam. Uma dessas ferramentas são as metaclasses, que fazem parte de um conceito de metaprogramação juntamente com os decorators. Para começarmos a falar sobre metaclasses, é importante entender alguns comportamentos básicos do Python. Veja só:
[python]
class A(object):
pass
$ print type(A)
>>> <type ‘type’>
$ print type(str)
>>> <type ‘type’>
$ print type([])
>>> <type ‘type’>
[/python]
Até aqui tudo bem: apenas criamos uma classe A (no new-object style), perguntamos qual é o tipo de A e descobrimos que A é do tipo type. Veja que interessante: str e list também são do tipo type. Conclusão: todos os objetos, por padrão, são do tipo type. Agora, veja que se usarmos a função isinstance para verificarmos se A é uma instância de type, descobrimos que isso é verdade. Conclusão 2: Todos os objetos são instâncias de type
[python]
print isinstance(A, A())
>>> True
print isinstance(A, type)
>>> True
[/python]
Logo: A() é uma instância de A, que por sua vez é uma instância de type. É importante entender que type é uma metaclasse; e metaclasses em Python funcionam como fábrica de classes, ou seja, elas criam instâncias de classes. Assim, podemos enriquecer a maneira como trabalhamos criando novas metaclasses. Para isso, basta fazer:
[python]
class MinhaMetaClasse(type): pass
class Usuario():
__metaclass__ = MinhaMetaClasse
[/python]
Essa é a notação para declarar que a minha classe Usuario, terá outra metaclasse como base, que não a type. Ok, até agora ficou claro como funciona o “instanciamento” de classes no Python, mas afinal qual situação eu poderia criar uma nova metaclasse? Alguns exemplos: metaclasse para fazer log de todos os métodos chamados, metaclasse para medir o tempo de execução de todos os métodos da classe que a utiliza, ou impossibilitar a criação de uma instância da classe que usa a metaclasse em questão. O código abaixo implementa  um Logger que fará print toda vez que qualquer método da classe Pessoa for chamado.
[python]
from inspect import disfunction
from date time import date time
def logger_decorator(func):
def log(*args, **kwargs):
print “Funcao %s foi chamada as %s com os parâmetros %s” % (func.__name__, datetime.now(), args)
return func(*args, **kwargs)
return log
class Logger(type):
def __init__(self, namespace, bases, attributes):
for key in attributes:
a_function = attributes[key]
if isfunction(a_function):
new_f = logger_decorator(a_function)
setattr(self, key, new_f) # mesma coisa que instancia.key = new_f
class Pessoa(object):
__metaclass__ = Logger
def ola(self, mensagem):
print mensagem
def sair(self):
print ‘tchau’
p = Pessoa() p.ola(‘blog’)
>>> “Funcao ola foi chamada as 2015-04-02 10:42:54.767371 com os parâmetros (<__main__.Pessoa object at 0x105c31550>, ‘dextra’)”>>> “dextra”
p.sair()
>>> “Funcao sair foi chamada as 2015-04-02 10:42:54.767509 com os parametros (<__main__.Pessoa object at 0x105c31550>,) ”
>>> “tchau”
[/python]
Referências:
Fluent Python (http://shop.oreilly.com/product/0636920032519.do)
Documentação Python (https://docs.python.org/2/reference/datamodel.html?highlight=metaclass#__metaclass__ , https://docs.python.org/3/reference/datamodel.html?highlight=metaclass#customizing-class-creation )

  • Receba nosso conteúdo em primeira mão.