有人知道这段代码有什么问题吗?
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
答案 0 :(得分:3)
paginated_instance_method
不是装饰器,它是返回装饰器的函数。所以
@paginated_instance_method()
def get_attending_users(self, *args, **kwargs):
(请注意括号。)
答案 1 :(得分:1)
你的装饰师有一个额外的间接水平,这会让事情失去意义。当你这样做时:
@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)
这就是装饰者所做的。请注意,调用paginated_instance_method
时会将get_attending_users
作为参数。这意味着在装饰器中,参数default_page_size
设置为函数paginated_instance_method
。您的装饰器返回函数wrap
,因此get_attending_users
设置为wrap
函数。
然后,当您再致电Event().get_attending_users()
时,它会调用wrap(self)
,其中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
采取参数。但即使你想使用该参数的默认值,你仍然必须调用 paginated_instance_method
。否则,您只需将方法作为参数传递,而不是paginated_instance_method
所期望的那样。
它为类方法“工作”的原因是classmethod将类作为第一个参数,而一个类(与实例不同) 具有__name__
属性。但是,我怀疑,如果你进一步测试它,你会发现它并没有真正做你想要它做的事情,因为它仍然包装了类而不是方法。
答案 2 :(得分:0)
这很简单,但在初看起来很棘手。请看pep 318。
@dec2
@dec1
def func(arg1, arg2, ...):
pass
这相当于:
def func(arg1, arg2, ...):
pass
func = dec2(dec1(func))
你有一个额外的包装器,它需要装饰器的args在包装函数(closure design pattern)中使用它。所以你的装饰师看起来像这样:
@dec(arg=True)
def func(arg1, arg2, ...):
pass
相当于:
def func(arg1, arg2, ...):
pass
func = dec(arg=True)(func)