Criador de Funções com Metaclasses
Esse é um projeto que fiz meses atrás para gerar funções em Python com operadores matemáticos usando metaclasses.
Com isso, você pode fazer:
g = f ** 2 + 1 #função que eleva ao quadrado e soma 1 g(10) == 101
O código abaixo é uma versão (muito) simplificada do módulo que coloquei no GitHub, mas serve como exemplo de uso de metaclasses em Python (neste caso, Python 3):
import operator
class MetaFuncBuilder(type):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
attr = '__{0}{1}__'
for op in (x for x in dir(operator) if not x.startswith('__')):
oper = getattr(operator, op)
op = op.rstrip('_') #special case for keywords: and_, or_
def func(self, *n, oper=oper):
return type(self)(lambda x: oper(self.func(x), *n),
self.op + [(oper.__name__, n[0])
if n else oper.__name__])
def rfunc(self, n, *, oper=oper):
return type(self)(lambda x: oper(n, self.func(x)),
self.op + [(n, oper.__name__)])
setattr(self, attr.format('', op), func)
setattr(self, attr.format('r', op), rfunc)
class FuncBuilder(metaclass=MetaFuncBuilder):
def __init__(self, func=None, op=None):
self.op = op if op else []
self.func = func if func else lambda x: x
def __repr__(self):
return '<var %s>' % self.op
def __call__(self, *args):
if not args:
return self.func() #unary operators
required, *args = args
out = self.func(required)
return out(*args) if args else out
f = FuncBuilder()
Em duas situações é comum o uso de metaclasses em Python: para que um certa classe tenha propriedades novas ou para automatizar algum mecanismo nas classes. Neste caso, usei para automatizar a criação de métodos para cada operador em Python, com ajuda do módulo operator.
Em Python, os operadores matemáticos binários existem nas formas __op__, __rop__ e __iop__, onde op é o nome, r significa que o objeto é o segundo operador e i significa in-place. Outros operadores existem em apenas uma forma.
Assim, o código pega os operadores e cria as três formas de cada um (os que existem em apenas uma forma ficam com 2 formas extras que não causam problema algum, já que não são nunca chamadas) e todos os operadores, ao invés de executar a operação designada, apenas criam uma novo objeto-função que pode ser chamado ou ter novas funções adicionadas.
O módulo no GitHub tem uma centena de outras funcionalidades para gerar essas funções, mas ele sofre de um problema que pode ser bem sério: Chamadas de função em Python são caras!
Basicamente, cada objeto do tipo FuncBuilder guarda a operação que tem que executar e o próximo da lista. Isso gera um monte de chamadas de função em cima dos operadores, o que pode deixar o código muito mais lento.
Minha próxima alteração pra esse código seria juntar todas as operações numa só chamada de função, o que não vai permitir extrair uma função de dentro de outra (como é possível agora por meio de closures), mas vai reduzir o custo para apenas uma chamada de função, como seria natural. Isso não vai ser tão simples por causa dessa estrutura recursiva da classe, mas quando tiver tempo, eu faço.


