as classes decoradas em Python
Pergunta
Eu quero construir classes para uso como decoradores com os seguintes princípios intactos:
- Deve ser possível colocar vários desses decoradores de classe no topo off 1 função.
- O ponteiro nome da função resultante deve ser indistinguível da mesma função sem um decorador, salvar talvez só por que tipo / classe que é.
- pedindo o decoradores não deve ser relevante a não ser que realmente mandatado pelos decoradores. Ie. decoradores independentes poderiam ser aplicados em qualquer ordem.
Isto é para um projeto Django, e no caso específico que eu estou trabalhando agora o método precisa de 2 decoradores, e para aparecer como uma função normal do Python:
@AccessCheck
@AutoTemplate
def view(request, item_id) {}
@AutoTemplate muda a função para que em vez de retornar um HttpResponse, ele só retorna um dicionário para utilização no contexto. A RequestContext é usado, e o nome do modelo é inferida a partir do nome do método e do módulo.
@AccessCheck adiciona verificações adicionais sobre o usuário com base no item_id.
Eu estou supondo que é apenas para obter o direito construtor e copiar os atributos adequados, mas que atributos são estes?
A seguir decorador não vai funcionar como eu descrevo:
class NullDecl (object):
def __init__ (self, func):
self.func = func
def __call__ (self, * args):
return self.func (*args)
Como demonstrado pelo seguinte código:
@NullDecl
@NullDecl
def decorated():
pass
def pure():
pass
# results in set(['func_closure', 'func_dict', '__get__', 'func_name',
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals'])
print set(dir(pure)) - set(dir(decorated));
Além disso, tentar e adicionar no construtor NullDecl, e ele vai trabalhar para o primeiro decorador, mas não a segunda "func impressão nome .." - como o nome vai estar ausente
Refined eduffy 's resposta um pouco, e parece funcionar muito bem:
class NullDecl (object):
def __init__ (self, func):
self.func = func
for n in set(dir(func)) - set(dir(self)):
setattr(self, n, getattr(func, n))
def __call__ (self, * args):
return self.func (*args)
def __repr__(self):
return self.func
Solução
A classe decorador fazer nada ficaria assim:
class NullDecl (object):
def __init__ (self, func):
self.func = func
for name in set(dir(func)) - set(dir(self)):
setattr(self, name, getattr(func, name))
def __call__ (self, *args):
return self.func (*args)
E então você pode aplicá-lo normalmente:
@NullDecl
def myFunc (x,y,z):
return (x+y)/z
Outras dicas
O decorador módulo ajuda você a escrever decoradores de preservação de assinatura.
E a PythonDecoratorLibrary pode fornecer exemplos úteis para decoradores.
Para criar um decorador que envolve funções em um assunto que torná-los indistinguível da função original, o uso functools.wraps
.
Exemplo:
def mydecorator(func):
@functools.wraps(func):
def _mydecorator(*args, **kwargs):
do_something()
try:
return func(*args, **kwargs)
finally:
clean_up()
return _mydecorator
# ... and with parameters
def mydecorator(param1, param2):
def _mydecorator(func):
@functools.wraps(func)
def __mydecorator(*args, **kwargs):
do_something(param1, param2)
try:
return func(*args, **kwargs)
finally:
clean_up()
return __mydecorator
return _mydecorator
(minha preferência pessoal é criar decoradores usando funções, não classes)
A ordenação dos decoradores é o seguinte:
@d1
@d2
def func():
pass
# is equivalent to
def func():
pass
func = d1(d2(func))