实例方法上的Python装饰器

有人知道这段代码有什么问题吗?

def paginated_instance_method(default_page_size=25):
    def wrap(func):
        @functools.wraps(func)
        def inner(self, page=1, page_size=default_page_size, *args, **kwargs):
            objects = func(self=self, *args, **kwargs)
            return _paginate(objects, page, page_size)
        return inner
    return wrap

class Event(object):
    ...
    @paginated_instance_method
    def get_attending_users(self, *args, **kwargs):
        return User.objects.filter(pk__in=self.attending_list)

我收到以下错误:

    Traceback (most recent call last):
      File "<console>", line 1, in <module>
      File "/Users/zarathustra/Virtual_Envs/hinge/hinge_services/hinge/api/decorators.py", line 108, in wrap
        def inner(self, page=1, page_size=default_page_size, *args, **kwargs):
      File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", line 33, in update_wrapper
        setattr(wrapper, attr, getattr(wrapped, attr))
    AttributeError: 'Event' object has no attribute '__name__'

我认为这行得通的原因是,通过反复试验,我得到了以下装饰器,就像类方法的魅力一样:

def paginated_class_method(default_page_size=25):
    def wrap(func):
        @functools.wraps(func)
        def inner(cls, page=1, page_size=default_page_size, *args, **kwargs):
            objects = func(cls=cls, *args, **kwargs)
            return _paginate(objects, page, page_size)
        return inner
    return wrap

解决方法:

您的装饰器具有额外的间接级别,可以间接解决问题.执行此操作时:

@paginated_instance_method
def get_attending_users(self, *args, **kwargs):
    return User.objects.filter(pk__in=self.attending_list)

您正在执行此操作:

def get_attending_users(self, *args, **kwargs):
    return User.objects.filter(pk__in=self.attending_list)
get_attending_users = paginated_instance_method(get_attending_users)

这就是装饰者所做的.请注意,使用get_attending_users作为参数调用paginated_instance_method.这意味着在您的装饰器中,参数default_page_size设置为函数paginated_instance_method.装饰器返回函数包装,因此将get_attending_users设置为该包装函数.

然后,当您随后调用Event().get_attending_users()时,它将调用wrap(self),其中self是您的Event实例. wrap期望参数是一个函数,并尝试返回一个包装该函数的新函数.但是参数不是函数,而是Event对象,因此functools.wrap尝试包装时会失败.

我有一种直觉,你想做的是这样的:

@paginated_instance_method()
def get_attending_users(self, *args, **kwargs):
    return User.objects.filter(pk__in=self.attending_list)

也就是说,您希望paginated_instance_method接受参数.但是,即使您要使用该参数的默认值,也仍然必须实际调用paginated_instance_method.否则,您仅将方法作为参数传递,这不是paginated_instance_method所期望的.

它对类方法“起作用”的原因是,类方法将类作为第一个参数,并且类(与实例不同)确实具有__name__属性.但是,我怀疑如果进一步测试它,您会发现它并没有真正按照您想要的去做,因为它仍然在包装类而不是方法.

上一篇:python-为什么无法使用“ from module import *”从模块导入此装饰器?


下一篇:如何使用python-decorator包来装饰类方法?