实例方法上的Python装饰器

2024-05-20 10:26:08 发布

您现在位置: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)

我得到以下错误:

^{pr2}$

我之所以认为这是可行的,是因为,通过反复试验,我让下面的装饰器像魅力一样为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

Tags: selfdefaultsizereturnobjectsdefpageargs
3条回答

这真的很简单,但乍一看很棘手。看pep 318。在

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

相当于:

^{pr2}$

您有一个额外的包装器,它接受一个装饰器的参数来在包装好的函数(closure design pattern)中使用它。所以你的装饰师看起来像这样:

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

相当于:

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

你的装饰师有一个额外的间接层次,那就是扔掉东西。执行此操作时:

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

你要做的是:

^{pr2}$

这就是装修工的工作。请注意,paginated_instance_method是用get_attending_users作为参数调用的。这意味着在decorator中,参数default_page_size被设置为函数paginated_instance_method。修饰符返回函数wrap,因此get_attending_users被设置为wrap函数。在

然后当您调用Event().get_attending_users()时,它将调用wrap(self),其中{}是您的事件实例。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接受一个参数。但即使您想使用该参数的默认值,您仍然必须实际调用callpaginated_instance_method。否则,只需将方法作为参数传递,这不是paginated_instance_method所期望的。在

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

paginated_instance_method不是decorator,它是一个函数,返回decorator。所以

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

(注意括号。)

相关问题 更多 >