218

部分適用はクールです。functools.partialラムダを介して取得できない機能は何ですか?

>>> sum = lambda x, y : x + y
>>> sum(1, 2)
3
>>> incr = lambda y : sum(1, y)
>>> incr(2)
3
>>> def sum2(x, y):
    return x + y

>>> incr2 = functools.partial(sum2, 1)
>>> incr2(4)
5

functoolsどういうわけかより効率的ですか、それとも読みやすいですか?

4

6 に答える 6

293

functools.partialラムダを介して取得できない機能は何ですか?

追加機能に関してはそれほど多くはありません(ただし、後で参照してください)。そして、読みやすさは見る人の目にかかっています。
関数型プログラミング言語 (特に Lisp/Scheme ファミリーの言語) に精通しているほとんどの人は、問題ないように見えlambdaます – 私は「ほとんど」と言いますが、すべてではありません。 ) しかしlambda、Python の目障りな異常と考えてください...
彼は、「Python の不具合」の 1 つとして、Python 3 から削除することを計画していたにもかかわらず、Python にそれを受け入れたことを悔い改めました。
私はそれで彼を完全にサポートしました。lambda (私はSchemeが大好きです... Pythonでは制限があり、奇妙な方法ではそうではありません'残りの言語で、私の肌をクロールさせます)。

しかし、多くの愛好家にとってはそうではありませんでしたlambda-- Python の歴史の中で最も反乱に近いものの 1 つを上演したのは、Guido が後戻りして撤退することを決定するまででし たlambda。など) は発生しませんでしたが (の機能を明示的に複製することを避けるため)、もちろん残っていました (完全な重複ではなく、目障りでもありません)。
functoolslambdapartial

の本体はlambdaに限定されているため、制限があることに注意してください。例えば...:

>>> import functools
>>> f = functools.partial(int, base=2)
>>> f.args
()
>>> f.func
<type 'int'>
>>> f.keywords
{'base': 2}
>>> 

functools.partialの返された関数は、イントロスペクションに役立つ属性で装飾されています。つまり、ラップしている関数と、その中で修正する位置引数と名前付き引数です。さらに、名前付き引数はすぐにオーバーライドできます (「修正」は、ある意味では、デフォルトの設定です)。

>>> f('23', base=10)
23

したがって、ご覧のとおり、明らかに単純ではありませんlambda s: int(s, base=2)!-)

はい、ラムダをゆがめてこれの一部を提供できます。たとえば、キーワードのオーバーライドについては、

>>> f = lambda s, **k: int(s, **dict({'base': 2}, **k))

しかし、最も熱烈な愛好家でさえ、この恐怖が呼び出しよりも読みやすいとは考えていないことを心から願っています!-)。「属性設定」の部分は、Python の「本体は単一の式」という制限があるため(さらに、代入は Python 式の一部にならないという事実に加えて)、さらに難しくなります...「式内で代入を偽造する」ことになります。リストの理解をその設計限界をはるかに超えて拡張することにより...:lambdapartiallambda

>>> f = [f for f in (lambda f: int(s, base=2),)
           if setattr(f, 'keywords', {'base': 2}) is None][0]

ここで、名前付き引数のオーバーライド可能性と 3 つの属性の設定を 1 つの式に結合し、それがどれほど読みやすいかを教えてください...!

于 2010-07-15T04:16:21.527 に答える
94

さて、違いを示す例を次に示します。

In [132]: sum = lambda x, y: x + y

In [133]: n = 5

In [134]: incr = lambda y: sum(n, y)

In [135]: incr2 = partial(sum, n)

In [136]: print incr(3), incr2(3)
8 8

In [137]: n = 9

In [138]: print incr(3), incr2(3)
12 8

Ivan Moore によるこれらの投稿では、Python の「ラムダの制限」とクロージャーについて詳しく説明しています。

于 2010-07-15T04:03:37.813 に答える
27

Python の最新バージョン (>=2.7) では、次のことはできますが、次のことはできpickleませpartiallambda

>>> pickle.dumps(partial(int))
'cfunctools\npartial\np0\n(c__builtin__\nint\np1\ntp2\nRp3\n(g1\n(tNNtp4\nb.'
>>> pickle.dumps(lambda x: int(x))
Traceback (most recent call last):
  File "<ipython-input-11-e32d5a050739>", line 1, in <module>
    pickle.dumps(lambda x: int(x))
  File "/usr/lib/python2.7/pickle.py", line 1374, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.7/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.7/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.7/pickle.py", line 748, in save_global
    (obj, module, name))
PicklingError: Can't pickle <function <lambda> at 0x1729aa0>: it's not found as __main__.<lambda>
于 2013-10-09T17:41:20.113 に答える
13

Alex が言及した追加機能に加えて、functools.partial のもう 1 つの利点は速度です。partial を使用すると、別のスタック フレームの構築 (および破棄) を回避できます。

パーシャルによって生成された関数もラムダも、デフォルトではドキュメント文字列を持っていません (ただし、 を介して任意のオブジェクトにドキュメント文字列を設定できます__doc__)。

詳細については、このブログを参照してください: Python での部分関数アプリケーション

于 2013-09-05T03:08:02.717 に答える
1

3 番目の例で最も早く意図を理解できます。

ラムダを解析すると、標準ライブラリが直接提供するよりも複雑/奇妙になることが予想されます。

sum2また、3 番目の例は;の完全な署名に依存しない唯一の例であることに気付くでしょう。したがって、結合がわずかに緩くなります。

于 2010-07-15T03:37:10.417 に答える