【问题标题】:Python: jQuery-like function chaining?Python:类 jQuery 函数链?
【发布时间】:2010-12-03 06:17:13
【问题描述】:

我在 Google 上找不到关于这个主题的任何内容,所以我想我应该在这里问一下:

是否可以像 jQuery 那样使用 Python 链接函数?

['my', 'list'].foo1(arg1, arg2).foo2(arg1, arg2).foo3(arg1, arg2) #etc...

当我编写这段代码时,我失去了很多空间和可读性:

foo3(foo2(foo1(['my', 'list'], arg1, arg2), arg1, arg2), arg1, arg2) #etc...

似乎存在一些用于创建此类函数的虚幻库,但我似乎不明白为什么它必须看起来如此复杂......

谢谢!

【问题讨论】:

  • 术语是“流畅的界面”。不知道为什么 GvR 不喜欢它们。

标签: python function chaining


【解决方案1】:

只要函数返回一个值,你就可以将它链接起来。在 jQuery 中,选择器方法通常返回选择器本身,这使您可以进行链接。如果你想在 python 中实现链接,你可以这样做:

class RoboPuppy:

  def bark(self):
    print "Yip!"
    return self

  def growl(self):
    print "Grr!"
    return self

pup = RoboPuppy()
pup.bark().growl().bark()  # Yip! Grr! Yip!

但是,您的问题似乎是您的函数参数过于狭窄。链接不是解决此问题的方法。如果你想压缩你的函数参数,只需将参数分配给变量,然后再将它们传递给函数,如下所示:

spam = foo(arg1, arg2)
eggs = bar(spam, arg1, arg2)
ham = foobar(eggs, args)

【讨论】:

  • :D 非常感谢!我什至不知道我的班级可以做到这一点!它适用于那种类型的函数,但我似乎无法将一个函数的返回传递给另一个函数......
  • 这似乎与此处的示例相符:en.wikipedia.org/wiki/Fluent_interface
【解决方案2】:

这是 Simon 的 ListMutator 建议的扩展:

class ListMutator(object):

    def __init__(self, seq):
        self.data = seq

    def foo1(self, arg1, arg2):
        self.data = [x + arg1 for x in self.data]
        # This allows chaining:
        return self

    def foo2(self, arg1, arg2):
        self.data = [x*arg1 for x in self.data]
        return self

if __name__ == "__main__":
    lm = ListMutator([1,2,3,4])
    lm.foo1(2, 0).foo2(10, 0)
    print lm.data

    # Or, if you really must:
    print ListMutator([1,2,3,4]).foo1(2, 0).foo2(10, 0).data

您可以做得更好,使用collections abstract base classes 使ListMutator 完全像一个列表一样。事实上,你可以继承list 本身,尽管它可能会限制你做某些你可能需要做的事情......而且我不知道对像list 这样的内置类型的子类化一般意见是什么。

【讨论】:

  • 只返回self 的问题在于,如果将它分配给不同的变量,它只是 Python 中的一个引用。在你的例子中。如果我们添加lm_two = lm并再次执行lm.foo1(2, 0).foo2(10, 0),我们将同时更改(lmlm_two。最好是return self.__class__(self.data)
【解决方案3】:

如果我们谈论的是对象方法,那么它是微不足道的,只是每个方法中的return self。另一方面,如果您想链接未绑定的函数,那么按照您想要的方式链接它们对我来说真的没有意义。当然,它看起来不错,但是它在语义上是不连贯的,因为“。”代表对象属性访问,而不是“链”。

【讨论】:

  • 嗯,所以你是说我不能定义这样的函数?我想我必须使用 jQuery 并为自己定义一个 L('my', 'strange', 'list') 类型函数。谢谢!
  • 作为一个题外话的问题,我可以在 Python 中的对象名称中使用哪些特殊字符?我试过$,但解释器不喜欢那样...
  • 嗯,从技术上讲你可能可以,因为函数也是 Python 中的对象。一个函数可以有一个自定义属性,它也是一个函数,可以像func1.func2(args) 一样调用。但这会是一团糟。
  • 从您的示例中,您似乎正在通过多个步骤更改列表。你为什么不走 pythonic 的方式,创建一个 ListMutator 类,它具有你需要的所有功能作为方法?
  • 我有一个类似于已经存在的类,但我必须初始化一个对象o 并嵌套这样的函数:o.foo(o.foo2(['the', 'list'])),但我知道我做错了什么,因为这比普通函数更糟糕......
【解决方案4】:

供将来参考:查看Moka,一个极简的函数式编程库。从他们的例子中:

(List()                    # Create a new instance of moka.List
   .extend(range(1,20))    # Insert the numbers from 1 to 20
   .keep(lambda x: x > 5)  # Keep only the numbers bigger than 5
   .rem(operator.gt, 7)    # Remove the numbers bigger than 7 using partial application
   .rem(eq=6)              # Remove the number 6 using the 'operator shortcut'
   .map(str)               # Call str on each numbers (Creating a list of string)
   .invoke('zfill', 3)     # Call zfill(x, 3) on each string (Filling some 0 on the left)
   .insert(0, 'I am')      # Insert the string 'I am' at the head of the list
   .join(' '))             # Joining every string of the list and separate them with a space.

>>> 'I am 007'

【讨论】:

    【解决方案5】:

    看看this。它是一个简单的链接包装类。它实现了一些 underscore.js 库的功能。你用下划线包裹你的列表、元组或字典,然后使用它,然后通过附加另一个下划线来从中获取值。

    print (_([1,2,3])
           .map(lambda x: x+1)
           .reverse()
           .dict_keys(["a", "b", "c"])
           .invert()
           .items()
           .append(("this is what you got", "chaining"))
           .dict()._)
    

    输出:

    {2: 'c', 3: 'b', 4: 'a', 'this is what you got': 'chaining'}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-06
      • 1970-01-01
      • 2016-12-26
      • 1970-01-01
      • 2013-01-04
      相关资源
      最近更新 更多