Подтвердить что ты не робот

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

(не путать с 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?

4b9b3361

Ответ 1

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

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

Ответ 2

Это возможно, если вы используете только чистые функции, чтобы методы не self.data непосредственно 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 OP.

Ответ 3

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

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