实例方法上的Python装饰器 [英] Python decorator on instance method

查看:277
本文介绍了实例方法上的Python装饰器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Anyone know what is wrong with this code?

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__'

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

The reason why I thought this would work is because, through trial and error, I got the following decorator working like a charm for classmethods:

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

推荐答案

您的装饰器具有额外的间接级别,这使事情一发不可收拾.当您这样做时:

Your decorator has an extra level of indirection which is throwing things off. When you do this:

@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.装饰器返回函数wrap,因此get_attending_users设置为该wrap函数.

That is what decorators do. Note that paginated_instance_method is called with get_attending_users as its argument. That means that in your decorator, the argument default_page_size is set to the function paginated_instance_method. Your decorator returns the function wrap, so get_attending_users is set to that wrap function.

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

Then when you then call Event().get_attending_users() it calls wrap(self), where self is your Event instance. wrap is expecting the argument to be a function, and tries to return a new function wrapping that function. But the argument isn't a function, it's an Event object, so functools.wrap fails when trying to wrap it.

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

I have a hunch that what you're trying to do is this:

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

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

That is, you want paginated_instance_method to take an argument. But even if you want to use the default value of that argument, you still have to actually call paginated_instance_method. Otherwise you just pass the method as the argument, which is not what paginated_instance_method is expecting.

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

The reason it "worked" for a classmethod is that a classmethod takes the class as the first argument, and a class (unlike an instance) does have a __name__ attribute. However, I suspect that if you test it further you'll find it's not really doing what you want it to do, as it's still wrapping the class rather than the method.

这篇关于实例方法上的Python装饰器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆