Então precisei de um decorator que em alguns momentos não receberia nenhum parâmetro, associando a função decorada a um sinal registrado no glade com o mesmo nome da função.
# Associa a função 'destroy' ao evento 'destroy' já registrado no glade
@handler
def destroy(self, *args):
print args
# Mesmo funcionamento
@handler()
def destroy(self, *args):
print args
Ou ainda, este mesmo decorator poderia receber argumentos detalhando qual o nome do evento e widget para quais a função decorada deve ser associada.
# Associa a função 'x' ao evento 'destroy' já registrado no glade
@handler('destroy')
def x(self, *args):
print args
# Associa a função y ao evento 'on_bt_ok_clicked' no widget bt_ok
@handler('on_bt_ok_clicked', bt_ok)
def y(self, *args):
print args
Quando nenhum argumento é informado (@decorator()) utilizo uma função aninhada para ser retornada como decorator e assim efetivamente decorar a função. O mesmo funcionamento se aplica quando os argumentos não são funções ou métodos (@decorator('x'), @decorator('x', a=1)), utilizando o módulo inspect . Esse comportamento só muda quando o decorator não recebe parâmetros, ou seja, quando este não é invocado, retornando a função ou método recebido sem nenhuma modificação.
def decorator(*args, **kargs):
import inspect
# @decorator() or @decorator('x') or @decorator('x', d='a')
if not args or not inspect.isroutine(args[0]):
def deco(func):
def wrapper(*f_args, **f_kargs):
return func(*f_args, **f_kargs)
return wrapper
return deco
elif inspect.isroutine(args[0]): # @decorator
return args[0]
Exemplos de uso:
@decorator
def d(x, y, d=1):
print x, y, d
#d = decorator(d)
@decorator()
def e(x, y, d=1):
print x, y, d
#e = decorator()(e)
@decorator('x')
def f(x, y, d=1):
print x, y, d
#f = decorator('x')(f)
@decorator('x', d=5)
def g(x, y, d=1):
print x, y, d
#f = decorator('x', d=5)(g)
Abaixo o mesmo decorator utilizando classe, assim o comportamento do decorator pode ser facilmente expandido para funcionar com métodos seguindo esse texto Decorators and Descriptors, fazendo o decorator ser ainda mais flexível podendo ser utilizado com funções ou métodos ao mesmo tempo:
class decorator(object):
def __init__(self, *args, **kargs):
import inspect
# @decorator() or @decorator('x') or @decorator('x', d='a')
if not args or not inspect.isroutine(args[0]):
def deco(func):
def wrapper(*f_args, **f_kargs):
return func(*f_args, **f_kargs)
return wrapper
self.func = deco
elif inspect.isroutine(args[0]): # @decorator
self.func = args[0]
def __call__(self, *args, **kargs):
return self.func(*args, **kargs)
def __get__(self, obj, type=None):
if obj is None:
return self
new_func = self.func.__get__(obj, type)
return self.__class__(new_func)
Exemplos de uso:
class A:
@decorator
def b(self, x, y):
print x, y
a = A()
a.b(1, 2)
@decorator
def b(x, y):
print x, y
b(2, 3)
2 comentários:
Essa coisa toda não está ficando muito "javanesca" não? Meu encanto pelo Python é proveniente, justamente, da limpeza do código (zen) Python e da minha frustração com aquela parafernalha verborrágica do Java.
Eu ainda curto Java e tenho sistemas escritos em Java, mas tudo o que posso (novo) estou fazendo em Python+GTK.
Dia desses estava ficando meio chateado com a sintaxe do binding PyGTK para acessar os widgets. Resolvi o problema escrevendo uma classe base que lê os widgets do arquivo glade e todo nome de componente que não é padrão (i.e. que não seja "button1" ou similar) ele associa à um atributo que é criado dinâmicamente na classe. Assim não tenho mais que me preocupar com o tal self.ui.get_widget('palavrao_enorme').
Talvez seja possível fazer com que os handlers sejam criados dinâmicamente seguindo o mesmo princípio. Ou não?
Re> Daniel...
Decorators em Python estão muito além de um simples "pattern" como em Java.
Decorators+funções de introspeção são muito poderosas. Os melhores frameworks escritos em Python utilizam muito desse tipo de implementação, tente fazer algo parecido em Java e você vai saber que essa implementação não pode ser chamada de "javanesca".
Claro que o código é meio complicado, mas afinal são pequenos "hacks" para melhorar ainda mais o poder da linguagem.
A sua sugestão de implementação é legal, e pode ser facilmente implementada com "__getattr__". Mas na minha lib eu queria uma API de uso menos intrusivo/automático.
Sobre criar os handlers dinamicamente, desconheço alguma maneira de pegar todos os sinais da "libglade", mas deve haver um maneira.
Postar um comentário