閉じる

【Python】イテレーションを効率化 (chain,islice,permutations)の使い方

大量のデータや複数のコレクションを扱うとき、毎回リストを作ってしまうとメモリと時間に無駄が出ます。

標準ライブラリのitertoolsは、イテレータのまま要素を流し込み、必要な分だけ取り出す設計になっているため、処理を軽くできます。

ここではchainislicepermutationsに絞って、初心者の方にもわかりやすく具体例で解説します。

itertoolsの基本

itertoolsとは?イテレーション効率化の標準ツール

itertoolsは、Python標準ライブラリに含まれるイテレータ用の便利ツール群です。

イテレータは要素を逐次的に生成する仕組みで、すべてを一度にメモリへ展開しません。

つまり「必要なときに必要な分だけ」計算する遅延評価を行うため、大きなデータでも省メモリで扱えます

本記事では次の3つにフォーカスします。

  • chain 複数イテラブルの連結
  • islice 一部だけの切り出し
  • permutations 順列の生成

importと基本の使い方

インポートは2通りあります。

必要な関数だけを明示的にインポートするのが読みやすくておすすめです。

Python
# 1. モジュールごと
import itertools

# 2. 必要な関数だけ(推奨)
from itertools import chain, islice, permutations

# 簡単なデモ: chainで連結 → isliceで先頭3件 → タプル化
data1 = [1, 2]
data2 = (3, 4)
first3 = tuple(islice(chain(data1, data2, range(5, 10)), 3))
print(first3)  # (1, 2, 3)
実行結果
(1, 2, 3)

イテレータは一度消費すると使い回せない(再利用不可)ことが多い点も頭に入れておくと安全です。

いつ使う?listより省メモリに

listへ変換すると全要素をメモリに展開します。

データが巨大な場合や、I/Oから逐次読み込む場合は、itertoolsでイテレータのまま処理する方が効率的です。

処理途中で必要な部分だけを見る、または早期に打ち切る用途に特に向いています。

chainの使い方(複数イテラブルを連結)

複数のリストやタプルを1つに連結

itertools.chainは、複数のイテラブルを「つなげて」一つの連続したシーケンスのように扱えます。

要素はコピーされず、順に読み出されるだけなので無駄がありません。

Python
from itertools import chain

a = [1, 2]
b = (3, 4)
c = "56"  # 文字列もイテラブル(1文字ずつ取り出されます)

# chainは与えた順に取り出します
for x in chain(a, b, c):
    print(x, end=" ")
print()
実行結果
1 2 3 4 5 6

文字列を1要素として扱いたい場合は[c]のようにリストに入れて渡します。

chain.from_iterableとの違い

chainは複数の引数を直接受け取りますが、chain.from_iterableは「イテラブルの中に、さらにイテラブルが入っている」形(ネスト)を1引数で受け取ります。

どちらも結果は同じです。

Python
from itertools import chain

nested = [[1, 2], (3, 4), range(5, 7)]

print(list(chain.from_iterable(nested)))  # ネストを1引数で
print(list(chain(*nested)))               # アンパックして複数引数で
実行結果
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6]

生成器(ジェネレータ)のように「順次イテラブルが出てくる」ケースではfrom_iterableが自然です。

Python
from itertools import chain

def gen():
    # 各イテレーションで「イテラブル」を1つずつ返す
    for i in range(3):
        yield range(i, i + 2)

print(list(chain.from_iterable(gen())))
実行結果
[0, 1, 1, 2, 2, 3]

listの+やextendとの違い

chainはイテレータを返し、要素をコピーしないのが最大の違いです。

+extendはリストに特化した操作で、データを実体化して結合します。

以下の比較を押さえておくと良いです。

方法戻り値/副作用メモリ備考
chain(a, b)イテレータ(非破壊)小さいあらゆるイテラブルを連結。1回きりの読み出しが基本
a + b新しいリスト結合分コピーリスト専用。元は変わらず新しく確保
a.extend(b)破壊的aを拡張必要分のみ確保リスト専用。元のaが変更される
Python
from itertools import chain

a = [1, 2]
b = [3, 4]

c = a + b            # 新しいリストを作る
print("c:", c)

a.extend(b)          # aを書き換える(破壊的)
print("a:", a)

it = chain([1, 2], [3, 4])  # 遅延評価のイテレータ
print(list(it))              # 必要になったときだけ展開
実行結果
c: [1, 2, 3, 4]
a: [1, 2, 3, 4]
[1, 2, 3, 4]

大量データでも省メモリ

巨大な配列を作らずに処理を先へ流せるのがchainの強みです。

次の例では、1千万件のrangeとジェネレータを連結していますが、先頭5件だけを取り出して即終了できます。

Python
from itertools import chain, islice

big1 = range(10_000_000)                 # rangeは遅延生成する
big2 = (x * x for x in range(10_000_000))  # 自前のジェネレータ

print(list(islice(chain(big1, big2), 5)))   # 先頭5件だけ
実行結果
[0, 1, 2, 3, 4]

「必要な分だけ」消費する設計により、メモリのピークを低く抑えられます。

isliceの使い方(一部だけ取り出す)

start stop stepの指定

isliceは、任意のイテラブルから範囲指定で要素を取り出す関数です。

islice(iterable, stop)またはislice(iterable, start, stop[, step])の形で使います。

stopは含まれない点はリストのスライスと同じです。

Python
from itertools import islice

print(list(islice(range(10), 4)))         # 0..3の4件
print(list(islice(range(10), 2, 9, 3)))   # 2,5,8 (start=2, stop=9, step=3)
実行結果
[0, 1, 2, 3]
[2, 5, 8]

listのスライスとの違い

isliceは「イテラブル」なら何にでも使え、遅延的に取り出すのが特徴です。

一方でlistのスライスはすでにあるリストから部分配列を新規作成します。

観点listのスライスitertools.islice
対象リストのみほぼ全イテラブル
メモリ新しいリストを作る遅延評価で必要分のみ
インデックス負数可、逆順可負数や逆順は不可
代入スライス代入可代入は不可(読み出しのみ)
Python
from itertools import islice

seq = [0, 1, 2, 3, 4, 5]

print(seq[2:5:2])                 # リストのスライス
print(list(islice(seq, 2, 5, 2))) # isliceでも同様に取り出せる
実行結果
[2, 4]
[2, 4]

先頭N件だけ読む(ファイル/ジェネレータ)

ファイルを全部読むのではなく、先頭だけを見るのにisliceは最適です。

ここではStringIOでファイルっぽい振る舞いを再現します。

Python
from io import StringIO
from itertools import islice

fake_file = StringIO("a\nb\nc\nd\ne\n")

for line in islice(fake_file, 3):   # 先頭3行だけ読む
    print(line.strip())
実行結果
a
b
c

CSVのヘッダ行スキップログの冒頭だけ確認など、日常的に役立ちます。

無限イテレーションの制御

itertools.countなどの無限イテレータもisliceで安全に区切れます。

Python
from itertools import count, islice

print(list(islice(count(10, 2), 5)))  # 10から2刻み、先頭5件
実行結果
[10, 12, 14, 16, 18]

「無限列×islice」で有限にするのは実務の常套手段です。

permutationsの使い方(順列を生成)

基本の使い方と戻り値

permutations(iterable, r=None)は、要素の順列をタプルとして生成するイテレータを返します。

戻り値はイテレータであり、各要素はタプルです。

Python
from itertools import permutations

items = ["A", "B", "C"]

for p in permutations(items):  # rを省略すると全長の順列(3! = 6通り)
    print(p)
実行結果
('A', 'B', 'C')
('A', 'C', 'B')
('B', 'A', 'C')
('B', 'C', 'A')
('C', 'A', 'B')
('C', 'B', 'A')

r引数で長さを指定

rを与えると、その長さの順列だけを生成します。

Python
from itertools import permutations

items = ["A", "B", "C"]

for p in permutations(items, 2):  # 3P2 = 6通り
    print("".join(p))
実行結果
AB
AC
BA
BC
CA
CB

重複要素の扱い

permutationsは「位置」を区別するため、入力に重複があると重複した順列も出力されます。

Python
from itertools import permutations

items = ["A", "A", "B"]

print(len(list(permutations(items))))     # 3! = 6 (重複を含む)
print(list(permutations(items, 2)))       # 重複したタプルが並ぶ

# ユニークな順列にしたいならsetを介す(大きい集合ではメモリに注意)
unique = sorted(set(permutations(items)))
print(unique)
実行結果
6
[('A', 'A'), ('A', 'B'), ('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'A')]
[('A', 'A', 'B'), ('A', 'B', 'A'), ('B', 'A', 'A')]

重複排除にはsetが手軽ですが、全順列を一度に展開するため入力が大きいとメモリを消費します。

組み合わせ爆発に注意

順列の数は階乗で増えます

少し大きいだけで現実的な計算量を超えます。

必ず「数を見積もる」か「isliceで一部だけ見る」などの対策を取りましょう。

Python
from math import factorial

for n in range(3, 8):
    print(f"n={n}, n!={factorial(n)}")
実行結果
n=3, n!=6
n=4, n!=24
n=5, n!=120
n=6, n!=720
n=7, n!=5040

先頭だけ確認するテクニックです。

Python
from itertools import permutations, islice

for p in islice(permutations(range(10), 3), 5):  # 10P3の先頭5件だけ
    print(p)
実行結果
(0, 1, 2)
(0, 1, 3)
(0, 1, 4)
(0, 1, 5)
(0, 1, 6)

「大きな順列はリスト化しない」「必要な分だけ消費する」を徹底すると安全です。

まとめ

itertoolsは「遅延評価」でイテレーションを効率化する標準ツールです。

chainは複数のイテラブルを無駄なく連結し、isliceは任意範囲だけを取り出し、permutationsは順列をタプルで生成します。

どれも「必要な分だけ使う」設計のため、巨大データや無限列でも安全に扱えます。

実務では、リスト化を最小限に抑え、isliceで早期に打ち切るのが鉄則です。

組み合わせ爆発しがちなpermutationsは特に規模の見積もり部分的なサンプリングを心がけてください。

慣れてくると、コードは短く、メモリと時間は軽く、そして意図は明確に表現できるようになります。

Python 実践TIPS - コーディング効率化・Pythonic
この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!