関数

【Python】ジェネレータ(generator)内包表記の使い方

【Python】ジェネレータ(generator)内包表記の使い方

Pythonのジェネレータ内包表記(dictionary comprehension)の使い方について解説します。

ジェネレータ内包表記(generator comprehension)

Pythonにおけるジェネレータ内包表記(generator comprehension)は、既存リスト等のイテラブル(iterable)なオブジェクトから、ジェネレータ関数が返却するようなジェネレータオブジェクトを作成する際にシンプルに記載するための定義方法です。

本記事では、ジェネレータ内包表記の基本的な使い方について紹介します。

なお、ジェネレータ関数については「ジェネレータ(generator)関数 ~yieldによる返却~」でまとめていますので興味があれば参考にしてください。

Note

イテラブル(iterable)なオブジェクトとは反復可能なオブジェクトでリスト(list)、タプル(tuple)、辞書(dict)等が代表的なものになります。また、__iter__()メソッドかもしくは__getitem__()メソッドを持つ任意のクラスのオブジェクトもfor文の繰り返しで使用することができます。

イテラブル(iterable)に関する公式ドキュメントの説明はこちらを参照してください。

ジェネレータ内包表記の使い方

ジェネレータ内包表記の構文は以下になります。

ジェネレータ内包表記
(式 for 仮変数 in イテラブルなオブジェクト if 条件)

上記の意味合いとしては、”イテラブルなオブジェクト”から要素を順番に取得し、”条件”に一致するものに対して”式”を適用した値を返却するようなジェネレータオブジェクトを作るということになります。

なお、”(“で始まるのでタプルの内包表記のように思われるかもしれませんが、タプルの内包表記というものはありません。タプルにする場合は、タプルに変換するという作業が必要になります。

ジェネレータ内包表記の使用例

以下の例を用いてジェネレータ内包表記の使い方を説明します。

gen = (i for i in range(10) if i % 2 == 0)

print(type(gen))
for i in gen:
    print(i)
【実行結果】
<class 'generator'>
0
2
4
6
8

上記例では、rangeで値を取得しつつ2で割り切れる数値のみを使ってgenというジェネレータを作成しています。もちろんrangeである必要はなくリストなどでもよいですし、条件も目的にあわせて変更することが可能です。

typeで確認している通り、ジェネレータ内包表記で記載した結果は<class ‘generator’>ということでジェネレータのオブジェクトになっていることが分かります。

ジェネレータオブジェクトを使うことで上記例のようにfor文の繰り返しに使ったり、next(gen)のように順に取り出して使用したりすることが可能になります。

タプルやリストに変換したい場合

()のみで記載すると上記で紹介したようにジェネレータのオブジェクトとなります。ジェネレータからタプルやリストに変換したい場合は、以下のように変換して使用することができます。

# ジェネレータからタプルに変換
gen = (i for i in range(10) if i % 2 == 0)
tpl_tmp = tuple(gen)
print(type(tpl_tmp), tpl_tmp)

# ジェネレータからリストに変換
gen = (i for i in range(10) if i % 2 == 0)
list_tmp = list(gen)
print(type(list_tmp), list_tmp)
【実行結果】
<class 'tuple'> (0, 2, 4, 6, 8)
<class 'list'> [0, 2, 4, 6, 8]

ジェネレータ内包表記を使用しないで記載する場合

上記で紹介してきた例をジェネレータ関数として同じように記載する場合は以下のようになります。

# ジェネレータ関数で定義
def generate_num():
    for g in range(10):
        if g % 2 == 0:
            yield g


gen = generate_num()
print(type(gen))
for i in gen:
    print(i)
【実行結果】
<class 'generator'>
0
2
4
6
8

ジェネレータ内包表記を積極的に使用するべき理由と注意点

上記でジェネレータ内包表記の基本的な使い方を紹介しました。

例を見てみると、ジェネレータ内包表記はシンプルに記載ができていることが分かったかと思います。また、ジェネレータ内包表記は、ジェネレータ内包表記を使用しない記載方法よりも処理速度が速いという特徴があるため、積極的に使用するように意識するとよいでしょう。

ただし、注意点として条件が複雑な場合等には、必ずしもジェネレータ内包表記を採用するべきではありません。

チームでの開発等をしている場合には、チームメンバーの他の人もソースを読むことになるため、読みやすく内容を理解しやすいように可読性を意識したコードを書くことが重要です。ジェネレータ内包表記を使うことでコードが複雑になってしまう恐れがある場合には、ジェネレータ内包表記を使用するべきかよく検討するようにしましょう。

まとめ

Pythonのジェネレータ内包表記(dictionary comprehension)の使い方について解説しました。

ジェネレータ内包表記は、既存リスト等のイテラブル(iterable)なオブジェクトから、ジェネレータ関数が返却するようなジェネレータオブジェクトを作成する際にシンプルに記載するための定義方法です。

ジェネレータ内包表記を用いるとシンプルな記載ができる上に、処理速度が高速であるという特徴があります。一方で、条件が複雑な場合等には、可読性が悪くなり、読みにくくなってしまうこともあるため、複雑なケースでは使用するべきかよく検討しましょう。

Pythonにおける内包表記は他にも「リスト内包表記」「辞書内包表記」「集合内包表記」といった種類があります。内包表記については、「内包表記(まとめ)」にまとめていますので興味があれば参考にしてください。