ProgrammingPython開発者

Pythonにおけるジェネレーターとは何か、どのように機能し、どのように利用され、リスト内包表現との違いは何かを説明してください。

Hintsage AIアシスタントで面接を突破

回答。

ジェネレーターは、Pythonにおける特別なイテラブルオブジェクトで、全コレクションを一度にメモリに占有せずに「オンザフライ」でシーケンスを作成することができます。yieldキーワードを使った関数や、ジェネレーター式((expr for ... in ...))によって実装されます。これは、大量のデータや無限のストリームを扱うときに便利です。

リスト内包表現との主な違い:

  • リスト内包表現[x for x in range(10)])は、メモリにすぐに全リストを作成します。
  • ジェネレーター(x for x in range(10)))は、一つずつ要素を生成し、はるかに少ないメモリを消費します。

ジェネレーターを使用するタイミング:

  • インデックスによる要素へのアクセスが不要な場合。
  • データがメモリに収容できるには大きすぎる場合。
  • データのストリーム処理を行う場合(例:ファイルの行を読み取る)。
# ジェネレーター関数 def counter(n): for i in range(n): yield i for number in counter(5): print(number)

ひっかけ問題。

yieldを使った関数と、リストを返す通常の関数の違いは何ですか?例を挙げてください。」

回答: 通常の関数は即座にリストを計算して返し、全要素のためにメモリを占有します。yieldを使った関数はジェネレーターを返し、一つずつ要素を出力し、全シーケンスをすぐにはメモリにロードしません。

def make_list(n): return [i for i in range(n)] # 即座にリストを返し、多くのメモリを占有します。 def make_generator(n): for i in range(n): yield i # 一つずつ要素を出力します。

このテーマの詳細を知らずに実際に起こったエラーの例。


物語

大きなログを解析するプロジェクトで、エラーメッセージを含む行を抽出するためにリスト内包表現が使用されました:

error_lines = [line for line in open('biglog.txt') if 'ERROR' in line]

ファイルは2GBを超えており、アプリケーションはOOM(Out of Memory)でクラッシュしました。ジェネレーターを使用する必要がありました:

error_lines = (line for line in open('biglog.txt') if 'ERROR' in line)

物語

従業員は短いリストを分析したいと考え、yieldを使った関数を書きましたが、戻り値がリストではなくジェネレーターであることを忘れていました:

result = my_generator_function() # resultはリストではなくジェネレーターです if len(result) > 5: # TypeError: 'generator'型のオブジェクトにはlen()がありません

修正:結果をlist()でラップします。


物語

ジェネレーターを何度もイテレートしようとしました:

numbers = (i for i in range(5)) for n in numbers: pass # ジェネレーターを使い果たしました for n in numbers: print(n) # 何も出力されません

ジェネレーターは一度きりです。再利用するには新しいものを作成する必要があります。