2

我有一個叫render方法,實際上是由子類實現,並呼籲像這樣:級聯功能和裝飾

class MyClass(object): 

    def __call__(self, *args, **kwargs): 

     api_response = self.render(*args, **kwargs) 

     return api_response 

    def render(self, *args, **kwargs): 
     """ This method is implemented by a subclass."""  
     raise NotImplementedError 

    def cascade_callables(): 
     print 'Ask awesome people at stackoverflow for the solution to this problem.' 

我可調用[c1, c2, c3]的列表。我想要做的cascade_callables方法裏面這樣的事情應該是這樣的:

def cascade_callables(): 
    callables = [c1, c2, c3] 
    callables.append(self.render) 
    self.render = reduce(some_decorator_that_accepts_two_functions, callables) 

所以基本上,我想我的render喜歡這個工作,而無需修改實際執行:

c1(*args, **kwargs) 
c2(*args, **kwargs) 
c3(*args, **kwargs) 
render(*args, **kwargs) 

我想是這樣的裝飾來爲我工作,我可以用在reduce

def cascade_modifiers(modifier1, modifier2): 

    def cascaded_modifier(self, *args, **kwargs): 

     modifier1(self, *args, **kwargs) 
     modifier2(self, *args, **kwargs) 

    return cascaded_modifier 

但時I g加時賽這樣的:

TypeError: cascaded_modifier() takes at least 1 argument (0 given) 

什麼是使用我試圖在這個問題解釋範式解決在Python 2.7這個問題的最好方法?

+2

......你爲什麼要這樣做?有沒有更簡單的方法?爲了對付子類(這對於可擴展性來說是一個非常糟糕的主意),而不是改變'self.render',你不能僅僅使子類實現,比如'_render',它被'render'調用。他們沒有超載?或者你不能實現'superrender'並且調用那個而不是'render'?你不能用子子類來繼承他們的MyClass子類並且重載那裏的行爲嗎?你在做什麼似乎是對我的最後一招。 – Veedrac 2014-09-11 15:44:25

回答

-2

您的cascade_modifier期望self作爲第一個參數。你爲什麼需要它? 你的裝飾器沒有收到你需要實際裝飾的功能對象。 而主要的東西 - 從接受函數的角度減少已有的裝飾器,改變它的行爲並返回新的函數。所以,你不需要擔心裝飾者通過減少。只需使用常規功能。

+0

「您的cascade_modifier希望自己成爲第一個參數,您爲什麼需要它? - 因爲它是一個實例方法,並且實例方法需要聲明一個'self'參數,並且實際上使用了參數,因此使其成爲模塊級函數不會刪除該參數的要求。此外,'reduce'不是一個裝飾器,並且不會從所提供的還原函數中構建新的函數。 (也就是說,尋找一個裝飾器來傳遞給'reduce'而不是其他類型的函數並不是一個好主意。) – user2357112 2014-09-11 06:00:17

0

爲什麼不讓子類實現不同的方法_render?然後render可以是

def render(self, *args, **kwargs): 
    for func in [c1, c2, c3, self._render]: 
     func(*args, **kwargs) 

這似乎更簡單。

-1

讓我們忘記你正在處理類方法一段時間,並談論你正在嘗試做的事情。

首先,你必須像

c1(*args, **kwargs) 
c2(*args, **kwargs) 
c3(*args, **kwargs) 
render(*args, **kwargs) # <- the decorator should not be placed here 

@render(*args, **kwargs) # <- it should be placed here 
c1(*args, **kwargs) 
c2(*args, **kwargs) 
c3(*args, **kwargs) 

你想要的是,上面的應該翻譯成

@renderc1(*args, **kwargs) 
c2(*args, **kwargs) 
c3(*args, **kwargs) 

然後

@renderc1c2(*args, **kwargs) 
c3(*args, **kwargs) 

然後

@renderc1c2(*args, **kwargs) 
c3(*args, **kwargs) 

記住,@運算符是

@render(*args, **kwargs) # <- it should be placed here 
c1(*args, **kwargs) 

只是語法糖變爲:

現在,讓我們把其他功能

@render(*args, **kwargs) # <- it should be placed here 
c1(*args, **kwargs) 
c2(*args, **kwargs) 
c3(*args, **kwargs) 

成爲

c1 = render(c1) 
c2(*args, **kwargs) 
c3(*args, **kwargs) 

這沒有意義。

你想要的是:

@render(*args, **kwargs) # <- it should be placed here 
c1(*args, **kwargs) 
@c1 
c2(*args, **kwargs) 
@c2 
c3(*args, **kwargs) 

這losely翻譯爲:

c1 = render(c1) 
c2 = c1(c2) 
c3 = c2(c3) 

其中,先前的功能,使下一個功能一個裝飾。問題是,你如何阻止最後一個函數變成裝飾器? 如果在某種程度上,如果ID功能,這可能是可能的(類似Haskell monad transforers)。但不確定如何實現這一點。

+1

我認爲你是從字面上理解「裝飾器」。提問者不在任何地方使用Python的裝飾器語法。我認爲「裝飾者」在這種情況下意味着只是一個函數,對其他函數起作用(並返回)。 – Blckknght 2014-09-11 07:29:15

2

您遇到的麻煩是您將新的render方法保存在實例變量中。這意味着它不會自動將self傳遞給它,因爲Python的方法綁定使用描述符協議,並且描述符僅在類變量時才起作用。

因此,您可能需要確保您的所有可調用對象都已綁定(如果他們需要使用self)並且將cascaded_modifier重寫爲不期望self參數。您實際上已經傳遞了原始render函數的綁定版本,所以在這種情況下它確實很好,您不會獲得self的第二個副本!

請注意,如果使用循環而不是reduce,則可以簡化cascade_callables。這種方法要求少了一個功能:

def cascade_callables(self): 
    callables = [c1, c2, c3] # these should be bound methods if they need self 
    callables.append(self.render) 

    def new_render(*args, **kwargs): # no self parameter here 
     for c in callables: 
      c(*args, **kwargs)  # nor here 

    self.render = new_render 

如果由於某種原因,你也需要通過self的可調用,而且沒有讓他們綁定方法的實用方法,你可以做的事情有點不同和使用self參數來自cascade_callables範圍:

def cascade_callables(self): 
    callables = [c1, c2, c3] # if these are not bound, we can work around the issue 
    old_render = self.render # this one is bound though so we can't mix it in 

    def new_render(*args, **kwargs): # no self parameter here 
     for c in callables: 
      c(self, *args, **kwargs) # but here we access the enclosing scope's self 
     old_render(*args, **kwargs) # this needs to be called separately in this version 

    self.render = new_render