2013-02-26 113 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)