2013-02-26 107 views
3

任何人都知道这段代码有什么问题吗?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__' 

为什么我认为这会工作是因为,通过试验和错误,我得到了下面的装饰像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 
+0

你的装饰就没有意义了。装饰器应该将一个函数作为参数,但是你只需要一个参数,这显然是一个数字('default_page_size')。你打算用'@paginated_instance_method(10)'来装饰吗? – BrenBarn 2013-02-26 20:23:36

+0

@BrenBarn是的,我试图做到这一点。我是新来的装饰者,但我会添加一个编辑来解释为什么我认为它会起作用。 – 2013-02-26 20:25:48

+0

你能展示你如何使用其他装饰器吗? – BrenBarn 2013-02-26 20:27:29

回答

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_methodget_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所期望的。

它的类方法“工作”的原因是,一类方法采取类作为第一个参数,一个类(不像一个实例)确实__name__属性。然而,我怀疑如果你进一步测试它,你会发现它并没有真正做你想做的事情,因为它仍然包装着类而不是方法。

+0

它在其他用法中有括号 - 这是重要的区别。所以它从来没有真正“工作”paginated_class_method。谢谢! – 2013-02-26 20:45:36

3

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

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

(注意括号)

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)