より好ましい:ラムダ関数またはネストされた関数(def
)?
通常の関数よりもラムダを使用する利点は1つあります。それらは式で作成されます。
いくつかの欠点があります:
- 名前なし(だけ
'<lambda>'
)
- ドキュメント文字列なし
- 注釈なし
- 複雑なステートメントはありません
また、どちらも同じタイプのオブジェクトです。これらの理由から、私は一般にdef
ラムダではなくキーワードを使用して関数を作成することを好みます。
最初のポイント-それらは同じタイプのオブジェクトです
ラムダは通常の関数と同じタイプのオブジェクトになります
>>> l = lambda: 0
>>> type(l)
<class 'function'>
>>> def foo(): return 0
...
>>> type(foo)
<class 'function'>
>>> type(foo) is type(l)
True
ラムダは関数であるため、ファーストクラスのオブジェクトです。
ラムダと関数の両方:
- 引数として渡すことができます(通常の関数と同じ)
- 外部関数内で作成されると、その外部関数のローカルのクロージャーになります
しかし、ラムダには、デフォルトで、関数が完全な関数定義構文を介して取得するいくつかのものが欠けています。
ランバ__name__
は'<lambda>'
ラムダは匿名関数であるため、自分の名前はわかりません。
>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'
したがって、ラムダは名前空間でプログラムで検索できません。
これは特定のものを制限します。たとえば、foo
シリアル化されたコードで検索できますが、次のことl
はできません。
>>> import pickle
>>> pickle.loads(pickle.dumps(l))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <function <lambda> at 0x7fbbc0464e18>:
attribute lookup <lambda> on __main__ failed
foo
自分の名前を知っているので、うまく検索できます。
>>> pickle.loads(pickle.dumps(foo))
<function foo at 0x7fbbbee79268>
ラムダには注釈も文書文字列もありません
基本的に、ラムダは文書化されていません。foo
より適切に文書化されるように書き換えましょう:
def foo() -> int:
"""a nullary function, returns 0 every time"""
return 0
今、fooにはドキュメントがあります:
>>> foo.__annotations__
{'return': <class 'int'>}
>>> help(foo)
Help on function foo in module __main__:
foo() -> int
a nullary function, returns 0 every time
一方、ラムダに同じ情報を提供する同じメカニズムはありません。
>>> help(l)
Help on function <lambda> in module __main__:
<lambda> lambda (...)
しかし、私たちはそれらをハックすることができます:
>>> l.__doc__ = 'nullary -> 0'
>>> l.__annotations__ = {'return': int}
>>> help(l)
Help on function <lambda> in module __main__:
<lambda> lambda ) -> in
nullary -> 0
しかし、おそらくヘルプの出力をめちゃくちゃにするいくつかのエラーがあります。
ラムダは式のみを返すことができます
ラムダは複雑なステートメントを返すことはできず、式のみを返すことができます。
>>> lambda: if True: 0
File "<stdin>", line 1
lambda: if True: 0
^
SyntaxError: invalid syntax
式は確かにかなり複雑になる可能性があり、非常に一生懸命努力すると、おそらくラムダで同じことを達成できますが、追加された複雑さは、明確なコードを書くことに対してより多くの不利益になります。
明快さと保守性のためにPythonを使用しています。ラムダの過剰使用はそれを防ぐことができます。
ラムダの唯一の利点:単一の式で作成できます
これが唯一の可能なアップサイドです。式を使用してラムダを作成できるため、関数呼び出し内でラムダを作成できます。
関数呼び出し内で関数を作成すると、他の場所で作成したものよりも(安価な)名前の検索を回避できます。
ただし、Pythonは厳密に評価されるため、名前の検索を回避することを除いて、そうすることによる他のパフォーマンスの向上はありません。
非常に単純な表現の場合、ラムダを選択する場合があります。
私はまた、インタラクティブなPythonを実行するときにラムダを使用する傾向があります。を呼び出すときにコンストラクタに引数を渡したい場合は、次のようなコード形式を使用しますtimeit.repeat
。
import timeit
def return_nullary_lambda(return_value=0):
return lambda: return_value
def return_nullary_function(return_value=0):
def nullary_fn():
return return_value
return nullary_fn
そして今:
>>> min(timeit.repeat(lambda: return_nullary_lambda(1)))
0.24312214995734394
>>> min(timeit.repeat(lambda: return_nullary_function(1)))
0.24894469301216304
上記のわずかな時間差は、名前の検索に起因しreturn_nullary_function
ていると思います。これはごくわずかです。
結論
ラムダは、特異点を作成するためにコード行を最小限にしたい非公式な状況に適しています。
ラムダは、特に重要でない場合に、後で来るコードの編集者を明確にする必要があるより正式な状況には適していません。
オブジェクトに適切な名前を付けることになっています。オブジェクトに名前がない場合、どうすればよいですか?
これらすべての理由から、私は一般にwith def
ではなくwithを使用して関数を作成することを好みますlambda
。
lambda
が、私は、これは「非常にまれ」であることを同意しない、それはに重要な機能のための共通であるsorted
かitertools.groupby
など、例えばsorted(['a1', 'b0'], key= lambda x: int(x[1]))