简体   繁体   English

实例方法上的 Python 装饰器

[英]Python decorator on instance method

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)

I get the following error:我收到以下错误:

    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:我认为这可行的原因是,通过反复试验,我得到了以下装饰器,就像 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

paginated_instance_method is not a decorator, it is a function that returns a decorator. paginated_instance_method不是装饰器,它是一个返回装饰器的函数。 So所以

@paginated_instance_method()
def get_attending_users(self, *args, **kwargs):

(Note the parentheses.) (注意括号。)

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)

You are doing this:你这样做:

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)

That is what decorators do.这就是装饰者所做的。 Note that paginated_instance_method is called with get_attending_users as its argument.请注意,调用paginated_instance_methodget_attending_users作为其参数。 That means that in your decorator, the argument default_page_size is set to the function get_attending_users .这意味着在您的装饰器中,参数default_page_size设置为函数get_attending_users Your decorator returns the function wrap , so get_attending_users is set to that wrap function.您的装饰器返回函数wrap ,因此get_attending_users设置为该wrap函数。

Then when you then call Event().get_attending_users() it calls wrap(self) , where self is your Event instance.然后,当您调用Event().get_attending_users()它会调用wrap(self) ,其中self是您的 Event 实例。 wrap is expecting the argument to be a function, and tries to return a new function wrapping that function. wrap期望参数是一个函数,并尝试返回一个包装该函数的新函数。 But the argument isn't a function, it's an Event object, so functools.wrap fails when trying to wrap it.但是参数不是一个函数,它是一个Event对象,所以functools.wrap在尝试包装它时失败。

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)

That is, you want paginated_instance_method to take an argument.也就是说,您希望paginated_instance_method接受一个参数。 But even if you want to use the default value of that argument, you still have to actually call paginated_instance_method .但即使您想使用该参数的默认值,您仍然必须实际调用paginated_instance_method Otherwise you just pass the method as the argument, which is not what paginated_instance_method is expecting.否则,您只需将该方法作为参数传递,这不是paginated_instance_method所期望的。

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.它对类方法“有效”的原因是类方法将类作为第一个参数,而类(与实例不同)确实具有__name__属性。 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.但是,我怀疑如果您进一步测试它,您会发现它并没有真正做您想要它做的事情,因为它仍在包装类而不是方法。

This is really easy, but tricky at first view.这真的很容易,但乍一看很棘手。 Look at pep 318 .看看pep 318

@dec2
@dec1
def func(arg1, arg2, ...):
    pass

This is equivalent to:这相当于:

def func(arg1, arg2, ...):
    pass
func = dec2(dec1(func))

You have an extra wrapper, which takes a decorator's args to use it in the wrapped functions (closure design pattern ).你有一个额外的包装器,它需要一个装饰器的 args 在被包装的函数中使用它(闭包设计模式)。 So your decorator will look like this:所以你的装饰器看起来像这样:

@dec(arg=True)
def func(arg1, arg2, ...):
    pass

Equivalent to:相当于:

def func(arg1, arg2, ...):
    pass
func = dec(arg=True)(func)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM