una - variables por argumento python




Usar el mismo decorador(con argumentos) con funciones y métodos (3)

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?


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)

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 ;-).


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?







decorator