一連の関数のリストを作る
>>> funcs = [lambda x: k * x for k in range(10)] >>> funcs[3](4) 36 >>> funcs[1](4) 36
これはリスト閉包内包がスコープを作らないので、それぞれの関数が「グローバルスコープのk」を参照しており、その値は関数を呼び出すときにはすでにループが最後まで回って4になってしまっていることが原因です。
まつもとさんは、Pythonの「デフォルト引数が定義時に評価される」という特徴を利用したイディオムを紹介してらっしゃいました。これはわかっている人間が自分だけで使うぶんにはクールで楽ですが、他の人が読むコードに使うのは好ましくないように思います。
>>> funcs = [lambda x, k = k: k * x for k in range(10)] >>> funcs[3](4) 12[lambda x: k * x for k in range(10)]がダメな理由は、lambdaの中のkがグローバルスコープのkを指しているからです。もう一枚関数で包めば(ローカル変数にしてやれば)期待通りに動くようになります。こちらの方がlambdaに慣れた人にはわかりやすい書き方かも知れません。
>>> funcs = [(lambda k: lambda x: k * x)(k) for k in range(10)] >>> funcs[3](4) 12でも、わかりにくいのはそもそも無理に1行で書こうとするからです。ワンライナーは悪。 おのおのの「呼び出せる」オブジェクトがそれぞれ別個の「かける数」を持つんですから、その通りに書けば何の問題もありません。
>>> class Multiplier: def __init__(self, k): self.k = k def __call__(self, x): return self.k * x >>> funcs = [Multiplier(k) for k in range(10)] >>> funcs[3](4) 12この最後の例が一番美しいと思うのは、僕がJavaに毒されているからでしょうか?(笑)