web-dev-qa-db-ja.com

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]
"""
_

1つの問題は、method(*args)を呼び出すと_self.o_が変更されてもNoneが返されない場合です。 (その後、selfを返すか、method(*args)が返すものを返す必要があります)。

誰かが連鎖を実装するより良い方法を持っていますか?それを行うには、おそらく多くの方法があります。

メソッドが常にNoneを返すと仮定するだけでよいので、常に_self.o_を返すことができますか?

24
robert king

私は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()

警告:これは、データを返すつもりのないクラス関数でのみ機能します。

お役に立てれば!

2
Sarang

非常に便利な Pipe ライブラリがあり、これがあなたの質問の答えになるかもしれません。例えば::

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

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はOPのchainではなくitertools.chainを指すことに注意してください。

16
reubano

その特定のメソッドがどのように機能するかを知らないと、そのメソッドが返す値の種類と理由を知ることができないため、オブジェクトのメソッドをチェーンできるようにする一般的な方法はありません。メソッドは、何らかの理由でNoneを返す場合があります。メソッドがオブジェクトを変更したことを常に意味するわけではありません。同様に、値を返すメソッドでも、連鎖可能な値が返されない場合があります。 list.indexのようなメソッドをチェーンする方法はありません。fakeList.index(1).sort()は機能する可能性があまりありません。indexの重要な点は、数値を返すということであり、その数値は何か、元のオブジェクトをチェーンするだけでは無視できません。

特定の特定のメソッド(並べ替えや削除など)をチェーン化するためにPythonの組み込み型をいじるだけの場合は、(ラッパークラスでオーバーライドすることにより)特定のメソッドを明示的にラップする方が、 __getattr__を使用した一般的なメカニズム。

4
BrenBarn