使用与函数和方法相同的装饰器(带参数)

Mat*_*kel 14 python methods arguments function decorator

我一直在尝试创建一个可以在python中与函数和方法一起使用的装饰器.它本身就不那么难,但是当创建一个带参数的装饰器时,它似乎就是这样.

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)
Run Code Online (Sandbox Code Playgroud)

上面的代码正确地包装了函数/方法,但是在方法的情况下,request参数是它正在操作的实例,而不是第一个非自身参数.

有没有办法判断装饰器是否应用于函数而不是方法,并相应地处理?

Ant*_*sma 17

扩展__get__方法.这可以推广到装饰器装饰器中.

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
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以让装饰器自动适应其使用的条件.

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
Run Code Online (Sandbox Code Playgroud)

请注意,所有函数调用都会调用包装函数,所以不要在那里做任何昂贵的事情.

装饰者的用法:

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
Run Code Online (Sandbox Code Playgroud)

  • 我发现这种方法仅适用于某些情况,并且在无法运行时极难调试.http://groups.google.com/group/django-developers/msg/f36976f5cfbcbeb3 (6认同)

Ale*_*lli 5

装饰器总是应用于一个函数对象 - 让装饰器print具有其参数的类型,你将能够确认它; 它通常应该返回一个函数对象(它已经是一个具有正确的装饰器__get__! - )虽然后者有例外.

即,在代码中:

class X(object):

  @deco
  def f(self): pass
Run Code Online (Sandbox Code Playgroud)

deco(f)在类体内调用,当你还在那里时,它f是一个函数,而不是方法类型的实例.(该方法是制造和返回f__get__时后f,作为的属性访问X或其实例).

也许你可以更好地解释你想要装饰的玩具,所以我们可以提供更多帮助......?

编辑:这也适用于带有参数的装饰器,即

class X(object):

  @deco(23)
  def f(self): pass
Run Code Online (Sandbox Code Playgroud)

那么它是deco(23)(f)在类体中调用的,f当作为参数传递给任何可调用的deco(23)返回时仍然是一个函数对象,并且该可调用的仍然应​​该返回一个函数对象(通常 - 有例外;-).


iro*_*ggy 5

由于您已经定义了__get__在绑定方法上使用装饰器的方法,因此可以传递一个标志,告诉它是否在方法或函数上使用它。

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)
Run Code Online (Sandbox Code Playgroud)