f
とlambda
関数は同じであるにもかかわらず、次の2つのリスト内包表記の出力が異なるのはなぜですか?
f = lambda x: x*x
[f(x) for x in range(10)]
そして
[lambda x: x*x for x in range(10)]
どちらも同じタイプtype(f)
をtype(lambda x: x*x)
返すことに注意してください。
[x*x for x in range(10)]
良いです。
f
とlambda
関数は同じであるにもかかわらず、次の2つのリスト内包表記の出力が異なるのはなぜですか?
f = lambda x: x*x
[f(x) for x in range(10)]
そして
[lambda x: x*x for x in range(10)]
どちらも同じタイプtype(f)
をtype(lambda x: x*x)
返すことに注意してください。
[x*x for x in range(10)]
良いです。
回答:
最初のものは、単一のラムダ関数を作成し、それを10回呼び出します。
2つ目は関数を呼び出しません。10の異なるラムダ関数を作成します。それらすべてをリストに入れます。最初のものと同等にするには、次のものが必要です。
[(lambda x: x*x)(x) for x in range(10)]
またはもっと良い:
[x*x for x in range(10)]
map(lambda x: x*x, range(10))
、そもそもOPが最初に意味したものでしょう。
list(map(lambda x: x*x, range(10)))
あなたを与えるだろう[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
この質問は、「有名な」「明白な」Python構文の非常に臭い部分に触れます-優先されるもの、ラムダ、またはfor forリストの理解。
OPの目的は0から9の正方形のリストを生成することではなかったと思います。その場合は、さらに多くの解決策を提供できます。
squares = []
for x in range(10): squares.append(x*x)
しかし、それは重要ではありません。重要なのは、W(hy)TFはこのあいまいな表現なので直観に反するのでしょうか。そして、私は最後にばかげたケースを持っているので、あまりにも早く私の答えを却下しないでください(私は就職の面接でそれをしました)。
したがって、OPの内包表記はラムダのリストを返しました。
[(lambda x: x*x) for x in range(10)]
もちろん、これは2乗関数の10個の異なるコピーです。以下を参照してください。
>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]
ラムダのメモリアドレスに注意してください -それらはすべて異なります!
もちろん、この式のより「最適な」バージョン(ハハ)を作成することもできます。
>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
見る?同じラムダの3倍。
変数_
として使用したことに注意してくださいfor
。それはx
in とは何の関係もありませんlambda
(字句的に影が薄くなっています!)。それを得る?
構文の優先順位がそうではない理由を説明しませんが、それはすべて次のことを意味します。
[lambda x: (x*x for x in range(10))]
これは、、[[0, 1, 4, ..., 81]]
または[(0, 1, 4, ..., 81)]
、または私が最も論理的であると思う場合、これはlist
1つの要素generator
のa-値を返します。それは事実ではありません、言語はこのように機能しません。
しかし、どうしたら...
for
変数に影を落とさずに、変数で使用する場合はどうなりますlambda
か???
さて、がらくたが起こります。これを見てください:
[lambda x: x * i for i in range(4)]
これはもちろん意味します:
[(lambda x: x * i) for i in range(4)]
しかし、それは意味しません:
[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]
これはクレイジーです!
リスト内包表記のラムダは、この内包表記の範囲を閉鎖するものです。字句彼らはを参照してクロージャ、i
それらが評価された際の基準ではなく、その値を経由して!
したがって、この式:
[(lambda x: x * i) for i in range(4)]
次とほぼ同等です:
[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]
Pythonデコンパイラー(つまり、dis
モジュールなど)を使用すると、ここでさらに多くを見ることができると思いますが、Python-VMに依存しない議論にはこれで十分です。就職の面接の質問はこれで終わりです。
list
では、実際に連続した整数を乗算する乗数ラムダを作成するにはどうすればよいですか?まあ、受け入れられた答えと同様に、リスト内包表現式の中で呼び出されているi
別lambda
のでラップすることで、直接のつながりを壊す必要があります。
前:
>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2
後:
>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1
(外側のラムダ変数も= i
でしたが、これがより明確な解決策であると判断しました-私y
たちは誰がどの魔女がどれであるかを確認できるように導入しました)。
2019-08-30を編集:
@sheridpによる回答にも含まれている@josolerの提案に従います-リスト内包の「ループ変数」の値はオブジェクト内に「埋め込む」ことができます-キーは適切なタイミングでアクセスするためのキーです。上記の「後」のセクションでは、別のコードでラップlambda
し、現在の値ですぐに呼び出していますi
。別の方法(少し読みやすい-'WAT'効果はありません)はi
、partial
オブジェクトの内部に値を格納し、「内部」(元の)lambda
にそれを引数として渡す(partial
オブジェクトでオブジェクトから渡される)呼び出しの時間)、すなわち:
2の後:
>>> from functools import partial
>>> a = [partial(lambda y, x: y * x, i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
すばらしいですが、まだ少しひねりがあります!コードリーダーで簡単にしたくない場合は、名前で(のキーワード引数として)因子を渡しpartial
ます。名前を変更しましょう:
2.5以降:
>>> a = [partial(lambda coef, x: coef * x, coef=i) for i in (1, 2)]
>>> a[0](1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() got multiple values for argument 'coef'
WAT?
>>> a[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'
待って...引数の数を1つ変更して、「多すぎる」から「少なすぎる」に変更しますか?
まあ、それは我々が通過したときに、本当のWATではありませんcoef
しpartial
、このように、それは、位置の後に来なければならないので、それは、キーワード引数になりx
そうのように、引数:
3の後:
>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
私はネストされたラムダよりも最後のバージョンを好みますが、それぞれ独自のものより...
[partial(lambda i, x: i * x, i) for i in (1, 2)]
人々は良い答えを出しましたが、私の意見の中で最も重要な部分を述べるのを忘れていました:2番目の例でX
は、リスト内包表記X
のlambda
機能は関数の機能と同じではなく、それらは完全に無関係です。したがって、2番目の例は実際には次のようになります。
[Lambda X: X*X for I in range(10)]
の内部反復 range(10)
は、リストに10個の類似のラムダ関数を作成することのみを担当します(10個の個別の関数ですが、完全に類似しています-各入力の2のべき乗を返します)。
一方、最初の例はまったく異なります。反復のXは結果と相互に作用するため、反復ごとに値は次のX*X
ようになるためです。[0,1,4,9,16,25, 36, 49, 64 ,81]
他の答えは正しいですが、後で実行できる、それぞれが異なるパラメーターを持つ関数のリストを作成しようとしている場合、次のコードがそれを行います。
import functools
a = [functools.partial(lambda x: x*x, x) for x in range(10)]
b = []
for i in a:
b.append(i())
In [26]: b
Out[26]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
この例は人為的なものですが、それぞれが異なるものを出力する関数のリストが必要な場合、つまり、
import functools
a = [functools.partial(lambda x: print(x), x) for x in range(10)]
for i in a:
i()
[lambda x: x*x for x in range(10)]
外部ループ関数fを繰り返し呼び出さないため、最初の関数より高速です。