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.'

我有一个 callables 列表[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

但我得到了这个:

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

使用我在这个问题中尝试解释的范式在 Python 2.7 中解决这个问题的最佳方法是什么?

4

4 回答 4

2

The trouble you're having is that you're saving your new render method in an instance variable. That means it won't get self passed to it automatically, as Python's method binding uses the descriptor protocol, and descriptors only work if they are class variables.

So, you probably need to make sure all your callables are already bound (if they need to use self) and to rewrite cascaded_modifier to not expect a self parameter. You actually already were passing a bound version of the original render function, so it's actually good in that case that you weren't going to get a second copy of self!

Note that can simplify cascade_callables if you use a loop rather than reduce. This approach requires one fewer function:

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

If for some reason you did need to pass self to the callables, and there was not a practical way to make them bound methods, you could do things a bit differently and use the self parameter from the enclosing cascade_callables scope:

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
于 2014-09-11T08:02:19.837 回答
0

为什么不让子类实现不同的方法_render呢?那么render可以

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

这似乎更简单。

于 2014-09-11T05:54:58.183 回答
-1

让我们暂时忘记您正在处理 Class 方法这一事实,并谈谈您正在尝试做的事情的要点。

首先,你有类似的东西

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)

这会丢失地转化为:

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

等等

其中,前一个函数使下一个函数成为装饰器。问题是,如何阻止最后一个函数成为装饰器?如果有某种ID函数,这可能是可能的(类似于 Haskell 中的 monad 转换器)。但不确定如何实现这一目标。

于 2014-09-11T07:05:21.497 回答
-2

您的 cascade_modifier 期望 self 作为第一个参数。你为什么需要它?您的装饰器没有收到您实际需要装饰的函数对象。最重要的是 - 就它接受函数、改变其行为并返回新函数而言,它已经是一种装饰器。所以,你不需要担心装饰器通过减少。只需使用常规功能。

于 2014-09-11T05:51:07.793 回答