procedimientos - pasar una lista como parametro en python
Usar el mismo decorador(con argumentos) con funciones y métodos (4)
Como ya está definiendo un __get__
para usar su decorador en el Método Bound, podría pasar una bandera diciéndole si se está utilizando en un método o función.
class methods(object):
def __init__(self, *_methods, called_on_method=False):
self.methods = _methods
self.called_on_method
def __call__(self, func):
if self.called_on_method:
def inner(self, request, *args, **kwargs):
print request
return func(request, *args, **kwargs)
else:
def inner(request, *args, **kwargs):
print request
return func(request, *args, **kwargs)
return inner
def __get__(self, obj, type=None):
if obj is None:
return self
new_func = self.func.__get__(obj, type)
return self.__class__(new_func, called_on_method=True)
He estado tratando de crear un decorador que se pueda usar con funciones y métodos en Python. Esto por sí solo no es tan difícil, pero cuando se crea un decorador que toma argumentos, parece serlo.
class methods(object):
def __init__(self, *_methods):
self.methods = _methods
def __call__(self, func):
def inner(request, *args, **kwargs):
print request
return func(request, *args, **kwargs)
return inner
def __get__(self, obj, type=None):
if obj is None:
return self
new_func = self.func.__get__(obj, type)
return self.__class__(new_func)
El código anterior ajusta la función / método correctamente, pero en el caso de un método, el argumento de request
es la instancia en la que está operando, no el primer argumento que no es propio.
¿Hay alguna forma de saber si el decorador se está aplicando a una función en lugar de a un método, y tratar de acuerdo con esto?
El decorador se aplica siempre a un objeto de función: haga que el decorador print
el tipo de argumento y podrá confirmarlo; y generalmente debería devolver también un objeto de función (que ya es un decorador con el apropiado __get__
! -) aunque hay excepciones a este último.
Es decir, en el código:
class X(object):
@deco
def f(self): pass
deco(f)
se llama dentro del cuerpo de la clase, y, mientras aún está allí, f
es una función, no una instancia de un tipo de método. (El método se fabrica y se devuelve en f
'' __get__
cuando más tarde se accede a f
como un atributo de X
o una instancia del mismo).
¿Tal vez puedas explicar mejor el uso de un juguete que quieras para tu decorador, para que podamos ser de más ayuda ...?
Editar : esto va para los decoradores con argumentos, también, es decir,
class X(object):
@deco(23)
def f(self): pass
entonces es deco(23)(f)
que se llama en el cuerpo de la clase, f
sigue siendo un objeto función cuando se pasa como el argumento a cualquier decorador invocable deco(23)
retorna, y ese invocable aún debe devolver un objeto función (generalmente - con excepciones ;-).
Para ampliar el enfoque __get__
. Esto se puede generalizar en un decorador decorador.
class _MethodDecoratorAdaptor(object):
def __init__(self, decorator, func):
self.decorator = decorator
self.func = func
def __call__(self, *args, **kwargs):
return self.decorator(self.func)(*args, **kwargs)
def __get__(self, instance, owner):
return self.decorator(self.func.__get__(instance, owner))
def auto_adapt_to_methods(decorator):
"""Allows you to use the same decorator on methods and functions,
hiding the self argument from the decorator."""
def adapt(func):
return _MethodDecoratorAdaptor(decorator, func)
return adapt
De esta forma, puedes hacer que tu decorador se adapte automáticamente a las condiciones en las que se usa.
def allowed(*allowed_methods):
@auto_adapt_to_methods
def wrapper(func):
def wrapped(request):
if request not in allowed_methods:
raise ValueError("Invalid method %s" % request)
return func(request)
return wrapped
return wrapper
Tenga en cuenta que se llama a la función de contenedor en todas las llamadas a funciones, por lo que no debe hacer nada caro allí.
Uso del decorador:
class Foo(object):
@allowed(''GET'', ''POST'')
def do(self, request):
print "Request %s on %s" % (request, self)
@allowed(''GET'')
def do(request):
print "Plain request %s" % request
Foo().do(''GET'') # Works
Foo().do(''POST'') # Raises
Una solución parcial (específica) que he encontrado se basa en el manejo de excepciones. Estoy intentando crear un decorador para permitir solo ciertos métodos de HttpRequest, pero hacerlo funcionar con ambas funciones que son vistas y métodos que son vistas.
Entonces, esta clase hará lo que yo quiero:
class methods(object):
def __init__(self, *_methods):
self.methods = _methods
def __call__(self, func):
@wraps(func)
def inner(*args, **kwargs):
try:
if args[0].method in self.methods:
return func(*args, **kwargs)
except AttributeError:
if args[1].method in self.methods:
return func(*args, **kwargs)
return HttpResponseMethodNotAllowed(self.methods)
return inner
Aquí están los dos casos de uso: decorar una función:
@methods("GET")
def view_func(request, *args, **kwargs):
pass
y métodos de decoración de una clase:
class ViewContainer(object):
# ...
@methods("GET", "PUT")
def object(self, request, pk, *args, **kwargs):
# stuff that needs a reference to self...
pass
¿Hay una solución mejor que usar el manejo de excepciones?