цепочка методов в python

(не путать с itertools.цепь)

Я читал следующее: 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:Python27python.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 ?

3 ответов


есть очень удобнаяPipe библиотека, которая может быть ответом на ваш вопрос. Например:

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

это возможно, если вы используете только чистые функции чтобы методы не изменялись 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 и не ОП chain.


не будет никакого общего способа разрешить какой-либо метод любого объекта быть прикованным, так как вы не можете знать, какое значение этот метод возвращает и почему, не зная, как этот конкретный метод работает. Методы могут возвращать None по любой причине; это не всегда означает, что метод изменяет объект. Аналогично, методы, которые возвращают значение, все равно могут не возвращать значение, которое может быть приковано. Нет способа связать такой метод, как list.index: fakeList.index(1).sort() не может иметь большие надежды работы, потому что весь смысл index он возвращает число, и это число что-то значит, и его нельзя игнорировать только для цепочки исходного объекта.

если вы просто возитесь со встроенными типами Python для цепочки определенных конкретных методов (например, sort и remove), вам лучше просто обернуть эти конкретные методы явно (переопределив их в своем классе-оболочке), а не пытаться сделать общий механизм с __getattr__.