27

(不要与 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

4

5 回答 5

22

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

seq = fib() | take_while(lambda x: x < 1000000) \
            | where(lambda x: x % 2) \
            | select(lambda x: x * x) \
            | sum()
于 2012-08-29T07:49:53.893 回答
16

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

这是一个使用带有列表的集合流水线的示例:

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.

于 2015-01-30T09:13:10.127 回答
9

我一直在为链接函数寻找类似的东西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()

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

希望这可以帮助!

于 2020-02-29T10:52:40.210 回答
4

不会有任何通用方法允许链接任何对象的任何方法,因为您无法知道该方法返回哪种值以及为什么不知道该特定方法如何工作。方法可能None因任何原因返回;这并不总是意味着该方法已经修改了对象。同样,确实返回值的方法仍然可能不会返回可以链接的值。没有办法链接这样的方法list.index:fakeList.index(1).sort()不能有太大的工作希望,因为它的重点index是它返回一个数字,这个数字意味着什么,不能仅仅链接到原始对象上而忽略它。

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

于 2012-08-29T07:40:11.563 回答
1

关于什么

def apply(data, *fns):
    return data.__class__(map(fns[-1], apply(data, *fns[:-1]))) if fns else data

>>> print(
...   apply(
...     [1,2,3], 
...     str, 
...     lambda x: {'1': 'one', '2': 'two', '3': 'three'}[x], 
...     str.upper))
['ONE', 'TWO', 'THREE']
>>> 

?

..甚至保留类型:)

于 2022-02-08T06:17:10.983 回答