【问题标题】:Call attribute after chaining methods链接方法后调用属性
【发布时间】:2012-03-17 17:14:12
【问题描述】:

我正在构建一个使用属性链的 Python 类。我试图弄清楚是否有一种方法可以识别何时调用链的最终属性并在该点执行一些处理代码。在调用最终的链式属性后,我想处理收集到的数据。我意识到可以在链的末尾显式调用处理属性,但如果可能的话,我想避免这种情况。

例如:

o = ObjectInstance()
# Note that the attribute calling order is subjective
o('data').method_a('other data').method_c('other_data') #o.process() is called here automatically

--更新--

我找到了一种针对我的情况的解决方法,尽管它不能回答根本问题。

对于我的特殊情况,我打算用一个实例分别处理多个链。通过覆盖我的类的__call__ 属性,我可以检查之前的链是否已被处理,并做出相应的反应。我已经计划有一个单独的渲染方法——它也可以处理以前的链——在处理完所有链之后,所以它适用于我的特定场景。

这个类看起来像:

class Chainable:

    current_chain_data = None
    processed_chains = list()


    def __call__(self, data):
        if self.current_chain_data:
            self.process()

        #Some logic
        self.current_chain_data = data
        return self


    def method_a(self, data):
        #Some logic
        self.current_chain_data = data
        return self

    def method_b(self, data):
        #...

    def process(self, data):
        #do stuff
        self.processed_chains.append(self.current_chain_data)
        self.current_chain_data = None

    def render(self):
        if self.current_chain_data:
            self.process()

        for c in self.processed_chains:
            output += c
        return output

并且可以像这样使用:

c = Chainable()

# do some chaining
c('data').method_a('other_data').method_c('other_data')

# previous chain is processed here, new chain started
c('additional_data').method_b('other_data') #...

# previous chain is processed here, output rendered
c.render()

【问题讨论】:

  • “如果可能的话,我想避免这种情况。”请不要。显式优于隐式。
  • 我知道您来自哪里,对于我在上面发布的简单示例,我完全同意。然而,就我的使用而言,关键目标是保持(尽可能准确)非 Python 脚本语言的熟悉语法。
  • 我建议您的解决方案可能会使您的用户感到困惑,因为它是不统一的。如果有人在调用c 之前调用c 上的另一个方法会发生什么?您的用户会期待这种行为吗?
  • 另外,您现在需要将 render 作为最后一个方法调用,否则您的最后一个链永远不会被处理。

标签: python chaining


【解决方案1】:

没有办法识别“最后”调用,因为“链”不是语言结构,而是统一语法与您自己的(良好)实践相结合的结果,即从非-函数式方法。

您的选择是:

  1. 按照您的建议进行最终流程调用(在许多方面是最干净的解决方案);
  2. 在每个阶段执行您的处理(这可能会或可能不会影响性能,具体取决于处理以及您的方法实际执行的操作);或
  3. 让每个方法定义一个关键字参数,以便您将其标记为最终调用,以触发处理。

我会建议选项 1,除非选项 2 自然不会产生性能损失,如果没有其他原因只是它引入了最小的复杂性,并且显式通常比隐式更好。

您覆盖__call__ 的解决方案有几个缺点:它可能会使您的用户感到困惑,因为它不统一;如果有人在调用c 之前在c 上调用了另一个方法,那么这种行为可能会让他们感到惊讶(链继续);最后,你仍然需要一个最终的render 调用来关闭最后一个链,这会使这样的代码变得比必要的更脆弱。

【讨论】:

  • 我认为您的观点都非常有效。我选择了最终处理调用的路线(在这种情况下,一个渲染方法),它将与该项目所构建的框架一起很好地工作(并且在语义上,我可能会添加)。我相信它不会让用户感到困惑,因为该类几乎完全反映了该类直接集成的非 Python 脚本语言的熟悉语法。谢谢!
  • @CaseyKinsey 酷!如果你想让用户使用 python,你可能还想提供一个普通的界面,让他们摆脱习惯(当然,这可能根本不适用于你的情况)。
【解决方案2】:
class DataObject:

        def __init__(self, data):

                self.data = data


        def method_a(self, data):

                self.data += data

                return self


        def method_c(self, data):

                self.data += data

                return self

        def process(self):

                print self.data

if __name__ == "__main__":

        o = DataObject('data')
        o.method_a('other data').method_c('other_data').process()

【讨论】:

  • 如果有明确的调用顺序,这将起作用,但正如我在问题中指出的那样,这是主观的。 method_c 很可能在 method_a 之前被调用。
  • @CaseyKinsey 在这种情况下,您可能必须显式调用 .process(),我已对其进行了编辑以反映这一点。
  • 是的,我在我的问题中指出,我可能不得不接受这一点。我确实为我在上面发布的非常具体的情况找到了一种解决方法,但我仍然很好奇是否有一种 Pythonic 方式可以以一种我不知道的更通用的方式来处理它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-21
  • 1970-01-01
  • 1970-01-01
  • 2017-11-28
  • 1970-01-01
  • 2016-09-04
  • 2012-01-27
相关资源
最近更新 更多