从装饰器传递位置参数时如何支持静态和 class 方法?

How to support static and class methods when passing positional arguments from a decorator?

我想编写一个装饰器,它在第一个位置传递一个额外的位置参数,但在 selfcls 之后传递给包装函数。

def handle_route(self, url, headers):
    pass

@require_login
def handle_route(self, user, url, headers):
    pass

既然有staticmethods、classmethods和normal methods,我怎么知道在什么位置传递附加参数?

import functools

def require_login(function):
    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        user = obtain_user(kwargs.get('headers', {}))
        return function(user, *args, **kwargs)
    return wrapper

这个例子只针对staticmethods。在其他情况下,用户作为 selfcls 参数传递导致错误。

最好使用装饰器class,而不是装饰器函数。然后您可以实现完整的 descriptor protocol,否则它是从函数继承的。

具体来说,您需要为您的对象创建一个 __get__ 方法。然后,您可以将 class-/static-/regular-method 的解析推迟到包装方法。

class RequireLogin(object):
  def __init__(self, wrapee):
    self.wrapee = wrapee
    self.user = 'foo' # changeme

  def __get__(self, instance, owner):
    def wrapped(*args, **kwargs):
      return self.wrapee.__get__(instance, owner)(self.user, *args, **kwargs)
      #                  ^ use class/static/regular method's __get__
    return wrapped