遅延評価(レイジー評価)とは、必要に応じてのみ値が計算される効率的なプログラミングの重要な概念です。歴史的に見ると、Pythonの主要な組み込み構造(リスト、タプル)は「貪欲」であり、すべての要素を事前に生成し、メモリに保存していました。データの量とストリーム処理の要求が増加する中で、遅延計算の必要性が生まれました。
問題点: 貪欲な計算は、フィルタリング、大きなコレクションの変換、またはファイルのストリーミングなど、段階的に結果を得ることができる状況で、メモリと時間の非効率的な使用を引き起こします。
解決策: Pythonには、生成器、イテレータ、標準ライブラリの関数(map、filter、zip、enumerate)やitertoolsモジュールなど、多くの遅延計算のツールが登場しました。これらはすべて、結果を一度に1つの値ずつ返す「遅延」オブジェクトを返します。
コードの例:
result = map(lambda x: x * x, range(100)) # 生成器イテレータを返す for y in result: print(y) # 値はイテレーションごとに計算される import itertools inf = itertools.count(1) for i in inf: if i > 3: break print(i) # 1, 2, 3
主な特徴:
Python3では、map/filter関数は常にリストを返しますか?
いいえ、Python 3ではこれらの関数はリストではなくイテレータを返します。リストを取得するには、結果をlist()でラップする必要があります。
x = map(int, ['1', '2']) # <map object> list(x) # [1, 2]
リストに変換せずにmapの結果の長さを取得することはできますか?
いいえ、イテレータは全要素を通過するまで、要素数を事前に知ることができません。list()を使用して計算する必要があり、これにより遅延性が失われます。
Python3のrange関数は貪欲ですか、それとも遅延ですか?
遅延です:rangeは「rangeオブジェクト」を作成し、全配列を保持せずに要求に応じて要素を「計算」します。
スクリプトが巨大なCSVファイルを処理し、list(open(f))を通じてすべての行のリストを作成します。大きなファイルの場合、サーバーはメモリ不足で「死ぬ」ことになります。
利点:
欠点:
コードが遅延処理を使用し、イテレータfor line in open(f)でファイルの行を通過したり、map/filterを通じて中間的なコレクションを生成せずに処理します。
利点:
欠点: