19

しばらく前に、Haskell のドキュメントを調べたところ、関数合成演算子が非常に優れていることがわかりました。だから私はこの小さなデコレータを実装しました:

from functools import partial

class _compfunc(partial):
    def __lshift__(self, y):
        f = lambda *args, **kwargs: self.func(y(*args, **kwargs)) 
        return _compfunc(f)

    def __rshift__(self, y):
        f = lambda *args, **kwargs: y(self.func(*args, **kwargs)) 
        return _compfunc(f)

def composable(f):
    return _compfunc(f)

@composable    
def f1(x):
    return x * 2

@composable
def f2(x):
    return  x + 3

@composable
def f3(x):
    return (-1) * x

@composable
def f4(a):
    return a + [0]

print (f1 >> f2 >> f3)(3) #-9
print (f4 >> f1)([1, 2]) #[1, 2, 0, 1, 2, 0]
print (f4 << f1)([1, 2]) #[1, 2, 1, 2, 0]

問題: 言語サポートがなければ、組み込み関数や次のようなラムダでこの構文を使用することはできません。

((lambda x: x + 3) >> abs)(2)

質問: 役に立つか? Pythonメーリングリストで議論する価値はありますか?

4

4 に答える 4

8

私見:いいえ、そうではありません。私は Haskell が好きですが、これは Python には合わないようです。(f1 >> f2 >> f3)あなたができる代わりに、それはcompose(f1, f2, f3)あなたの問題を解決します-コアをオーバーロード、装飾、または変更することなく、任意の呼び出し可能オブジェクトで使用できます(IIRCの誰かがfunctools.compose少なくとも一度提案しました;私は今それを見つけることができません)。

さらに、言語定義は現在凍結されているため、おそらくそのような変更は拒否されるでしょう - PEP 3003を参照してください。

于 2010-02-17T15:20:30.993 に答える
6

関数合成は、Python で非常に一般的な操作ではありません。特に、合成演算子が明らかに必要な方法ではありません。<<何かが追加されたとしても、Python のとforの選択が好きかどうかは>>わかりませんが、それはあなたにとってそう思われるほど明白ではありません。

多くの人は、関数composeの順序に問題がない方が快適だと思います。compose(f, g)(x)つまり、数学やHaskellとf(g(x))同じ順序になります。Python は、特に特殊文字が広く知られている意味を持っていない場合に、英語の単語で使用できる場合は句読点を使用しないようにします。(デコレータの @ や関数の引数の * や ** など、見逃すにはあまりにも有用と思われるものについては例外が設けられています。)o.

これを python-ideas に送信することを選択した場合、stdlib または一般的な Python ライブラリで、関数合成によってコードがより明確になり、書きやすく、保守しやすくなり、または効率的です。

于 2010-02-17T17:54:59.937 に答える
6

で実行できますがreduce、呼び出しの順序は左から右のみです。

def f1(a):
    return a+1

def f2(a):
    return a+10

def f3(a):
    return a+100

def call(a,f):
    return f(a)


reduce(call, (f1, f2, f3), 5)
# 5 -> f1 -> f2 -> f3 -> 116
reduce(call, ((lambda x: x+3), abs), 2)
# 5
于 2011-10-07T14:31:46.140 に答える
1

私は、言語の変更が価値があるかどうかについての見解を得るのに十分なPythonの経験がありません。しかし、私は現在の言語で利用可能なオプションについて説明したいと思いました。

予期しない動作の作成を回避するために、関数の合成は、理想的には標準の数学(またはHaskell)の演算の順序に従う必要があります。つまり、f ∘ g ∘ happly h、then g、thenの順に意味する必要がありますf

Pythonで既存の演算子を使用する場合は、たとえば<<、ラムダとビルトインに問題があるとおっしゃっています。__rlshift__に加えてリフレクトバージョンを定義することで、あなたの生活を楽にすることができます__lshift__composableこれにより、オブジェクトに隣接するラムダ/ビルトインが処理されます。隣接するラムダ/ビルトインが2つある場合はcomposable、@ si14が提案しているように、それら(1つだけ)を明示的に変換する必要があります。注意してください、私は本当に意味します__rlshift__、ではありません__rshift__; __rshift__実際、演算子の形状によって方向性のヒントが提供されているにもかかわらず、順序の変更は混乱を招くため、使用しないことをお勧めします。

しかし、検討したい別のアプローチがあります。Ferdinand Jamitzkyには、組み込みでも機能するPythonで疑似中置演算子を定義するための優れたレシピがあります。f |o| gこれにより、実際には非常に合理的に見える関数合成のために書くことができます。

于 2012-12-05T16:48:40.417 に答える