部分適用はクールです。どの機能が 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
はどういうわけかより効率的ですか、それとも読み取り可能ですか?
さて、ここに違いを示す例があります:
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のクロージャについて詳しく説明しています。
Python(> = 2.7)の最新バージョンでは、pickle
をpartial
にできますが、lambda
にはできません:
>>> 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>
Functoolsはどういうわけかより効率的ですか?
これに対する部分的な答えとして、パフォーマンスをテストすることにしました。これが私の例です:
from functools import partial
import time, math
def make_lambda():
x = 1.3
return lambda: math.sin(x)
def make_partial():
x = 1.3
return partial(math.sin, x)
Iter = 10**7
start = time.clock()
for i in range(0, Iter):
l = make_lambda()
stop = time.clock()
print('lambda creation time {}'.format(stop - start))
start = time.clock()
for i in range(0, Iter):
l()
stop = time.clock()
print('lambda execution time {}'.format(stop - start))
start = time.clock()
for i in range(0, Iter):
p = make_partial()
stop = time.clock()
print('partial creation time {}'.format(stop - start))
start = time.clock()
for i in range(0, Iter):
p()
stop = time.clock()
print('partial execution time {}'.format(stop - start))
on Python 3.3与えます:
lambda creation time 3.1743163756961392
lambda execution time 3.040552701787919
partial creation time 3.514482823352731
partial execution time 1.7113973411608114
つまり、パーシャルは作成にもう少し時間が必要ですが、実行にはかなり時間がかかります。これは、 ars の回答で説明されているアーリーバインディングとレイトバインディングの効果です。
アレックスが言及した追加の機能に加えて、functools.partialのもう1つの利点は速度です。 partialを使用すると、別のスタックフレームの構築(および破棄)を回避できます。
デフォルトでは、パーシャルでもラムダでも生成された関数にはdocstringがありません(ただし、__doc__
)。
このブログで詳細を見つけることができます: Pythonの部分関数アプリケーション
3番目の例で意図を最も早く理解します。
ラムダを解析するとき、標準ライブラリが直接提供するよりも複雑/奇妙なものを期待しています。
また、3番目の例がsum2
の完全な署名に依存しない唯一の例であることに気付くでしょう。したがって、わずかに疎結合になります。