nikkie-ftnextの日記

イベントレポートや読書メモを発信

itertools.chain() と itertools.chain.from_iterable() の呼び出し方の違い

Today(※最近) I learnedな小ネタです

目次

Pythonのitertools.chainで混乱しがち

先日書いた、ネストしたfor文を全部抜けるにはジェネレータを使うという話

ジェネレータ関数を自前で定義する代わりに、itertools.chain.from_iterable()も取り上げました。
2重のリストから1, 2, 3, 4, と1つずつ取り出すイテレータを作っています。

from itertools import chain

for element in chain.from_iterable([[1, 2], [3, 4], [5, 6]]):
    print(element)
    if element == 3:
        break

itertools.chain.from_iterable()の他に、itertools.chain()もあり、「これってそれぞれどう呼び出すんだっけ?」と混乱しがちなので、今回向き合います。

itertools.chainはクラス

まずitertools.chainクラスです。

>>> # この記事では Python 3.12.6
>>> import itertools
>>> type(itertools.chain)
<class 'type'>

>>> type(str)
<class 'type'>
>>> class C: ...
>>> type(C)
<class 'type'>

これを知ったことで、私はitertools.chain()itertools.chain.from_iterable()に向き合いやすくなりました

itertoolsのドキュメントより、引数のシグネチャ

ドキュメントから、それぞれの引数のシグネチャを確認します

  • itertools.chain(*iterables)
    • イテラブル1可変長個(=何個でも)受け取ります
    • itertools.chain([1, 2], [3, 4], [5, 6])のような初期化になりますね
      • 3つのイテラブルを渡しています
  • itertools.chain.from_iterable(iterable)
    • たった1つのイテラブルを受け取ります
    • 初期化は itertools.chain.from_iterable([[1, 2], [3, 4], [5, 6]])
      • 渡したイテラブルは1つです
      • 中の要素にイテラブルを持っていますね

ここまでに確認したことから、以下のように書けます

  • 1例目:1つのイテラブルをitertools.chain.from_iterable()に渡します
  • 2例目:複数のイテラブルをitertools.chain()に渡します
  • 3例目
    • イテラブルのイテラブルであることを利用した例です
    • イテラブルのイテラブルを*でアンパックして、複数のイテラブルをitertools.chain()に渡しています

終わりに

混乱していたitertools.chain()itertools.chain.from_iterable()の呼び出し方を完全に理解しました!

  • itertools.chainはクラス
  • itertools.chain()はイニシャライザで、任意個のイテラブルを受け取る
  • itertools.chain.from_iterable()はクラスメソッドで、イテラブルのイテラブル1つを受け取る
    • 1つのイテラブルから だからfrom_iterableという命名なのかも(from single iterableの意が込められている説)

イテラブルがいくつか手元にあるときに少ないコード量でイテレータを作れるので、2通りの呼び出し方を完全に理解したitertools.chainは役に立ちそうです


  1. 「イテラブル」を扱った過去記事