【问题标题】:method chaining in pythonpython中的方法链接
【发布时间】:2012-08-23 18:26:11
【问题描述】:

(不要与 itertools.chain 混淆)

我正在阅读以下内容: http://en.wikipedia.org/wiki/Method_chaining

我的问题是:在 python 中实现方法链的最佳方式是什么?

这是我的尝试:

class chain():
    def __init__(self, my_object):
        self.o = my_object

    def __getattr__(self, attr):
        x = getattr(self.o, attr)
        if hasattr(x, '__call__'):
            method = x
            return lambda *args: self if method(*args) is None else method(*args)
        else:
            prop = x
            return prop

list_ = chain([1, 2, 3, 0])
print list_.extend([9, 5]).sort().reverse()

"""
C:\Python27\python.exe C:/Users/Robert/PycharmProjects/contests/sof.py
[9, 5, 3, 2, 1, 0]
"""

一个问题是如果调用method(*args) 修改self.o 但不返回None。 (那么我应该返回self 还是返回method(*args) 返回的内容)。

有没有人有更好的方法来实现链接?可能有很多方法可以做到这一点。

我是否应该假设一个方法总是返回None,所以我可能总是返回self.o

【问题讨论】:

  • (注意我不确定是否应该在 python 中使用方法链接,但我仍然感兴趣)
  • 您应该使用pure functions,这样方法就不会直接修改self.o,而是返回修改后的版本。此外,__getattr__ 应该返回一个链实例。

标签: python method-chaining


【解决方案1】:

有一个非常方便的Pipe 库,它可能是您问题的答案。例如::

seq = fib() | take_while(lambda x: x < 1000000) \
            | where(lambda x: x % 2) \
            | select(lambda x: x * x) \
            | sum()

【讨论】:

  • +1 在习惯了 C# 的 LINQ 之后,Pipe 是我在 Python 中导入的第一件事。 (不过你需要小心from Pipe import *
  • 无论如何,Pipe 用于组合函数 - 将函数 A 的结果作为函数 B 的参数传递。链接(通常)是关于从多个调用返回相同的对象以进行多次修改在一条链中。 JQuery 已经普及了很多。
  • 是的。但我认为在 Python 中不应该像在 JavaScript 中那样使用链接。
  • 谢谢,我将其标记为正确只是因为我通过阅读管道的源代码学到了一些很酷的东西。
  • @robertking, @Kos, @BasicWolf 请参阅下面的my answer,这是Pipe lib 的基本且更简单的实现。
【解决方案2】:

我一直在寻找类似的东西来链接Class 函数,但没有找到好的答案,所以这是我所做的并认为是一种非常简单的链接方式:只需返回 self 对象。

这是我的设置:

Class Car():
    def __init__(self, name=None):
        self.name = name
        self.mode = 'init'

    def set_name(self, name):
        self.name = name
        return self

    def drive(self):
        self.mode = 'drive'
        return self

现在我可以通过调用来命名汽车并将其置于行驶状态:

my_car = Car()
my_car.set_name('Porche').drive()

警告:这只适用于不打算返回任何数据的类函数。

希望这会有所帮助!

【讨论】:

  • 是的,这是有道理的——我认为链接只对控制自身状态但公开交互方法的对象有意义。接收数据通常应该是在修改状态的过程之后单独调用,否则没有明确的抽象
【解决方案3】:

如果您只使用pure functions,这样方法就不会直接修改self.data,而是返回修改后的版本,这是可能的。您还必须返回 Chainable 实例。

这是一个使用 collection pipelining 和列表的示例:

import itertools

try:
    import builtins
except ImportError:
    import __builtin__ as builtins


class Chainable(object):
    def __init__(self, data, method=None):
        self.data = data
        self.method = method

    def __getattr__(self, name):
        try:
            method = getattr(self.data, name)
        except AttributeError:
            try:
                method = getattr(builtins, name)
            except AttributeError:
                method = getattr(itertools, name)

        return Chainable(self.data, method)

    def __call__(self, *args, **kwargs):
        try:
            return Chainable(list(self.method(self.data, *args, **kwargs)))
        except TypeError:
            return Chainable(list(self.method(args[0], self.data, **kwargs)))

像这样使用它:

chainable_list = Chainable([3, 1, 2, 0])
(chainable_list
    .chain([11,8,6,7,9,4,5])
    .sorted()
    .reversed()
    .ifilter(lambda x: x%2)
    .islice(3)
    .data)
>> [11, 9, 7]

请注意,.chain 指的是 itertools.chain,而不是 OP 的 chain

【讨论】:

    【解决方案4】:

    不会有任何通用的方法允许任何对象的任何方法被链接,因为如果不知道该特定方法的工作原理,您无法知道该方法返回什么类型的值以及为什么返回。方法可能出于任何原因返回None;这并不总是意味着该方法已经修改了对象。同样,确实返回值的方法仍然可能不会返回可以链接的值。没有办法链接像list.index 这样的方法:fakeList.index(1).sort() 工作的希望不大,因为index 的全部意义在于它返回一个数字,而这个数字意味着什么,不能忽略只是为了链接原始对象。

    如果您只是摆弄 Python 的内置类型来链接某些特定方法(如排序和删除),您最好直接包装这些特定方法(通过在包装类中覆盖它们),而不是尝试用__getattr__做一个通用机制。

    【讨论】:

    • 谢谢 - 你是对的。我不认为这是可能的一般。我能想到的最好的办法是让程序员决定他们是否要链接。例如。默认情况下它不返回 self(它只返回方法返回的内容)但是如果程序员明确地使用 list_.chain.sort() 它可能会返回 list_ 而不是 None。
    • @robertking:是的,一般的交易是方法链不是你可以轻易附加到现有对象的东西。您基本上必须设计一个考虑到方法链的类才能正常工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多