【Python初心者】繰り返し可能オブジェクト（イテラブル）を4つの分類で整理してみた

Pythonでfor文を使って繰り返し処理を行うとき、`list` や `str` のような「繰り返し可能なオブジェクト（イテラブル）」をよく使います。  
この記事では、その「イテラブル」の正体と、代表的な4つの分類について学習の記録としてまとめてみます。

## 1. イテラブルとは何か？

イテラブル（`Iterable`）とは、次のような特徴を持つオブジェクトのことです：

- `__iter__()` メソッドを持っている（または `collections.abc.Iterable` を満たす）
- `for` 文や `in` 演算子で繰り返し処理できる

つまり、for文で `for x in obj:` のように書けるオブジェクトは、すべてイテラブルです。

## 2. イテラブルの4つの分類

イテラブルには、以下のような主な分類があります：

| 種類 | 特徴 | 例 | 備考 |
|------|------|----|------|
| **シーケンス型イテラブル** | 順序あり・インデックスアクセス可 | `list`, `tuple`, `str`, `range` | `__getitem__()` を持つ／何度でも使える |
| **非シーケンス型イテラブル** | 順序なし・インデックス不可 | `set`, `dict`（キー） | 順序保証なし（dictは3.7以降順序保持） |
| **イテレータ** | `__next__()` を持つ・使い切り | `iter(list)`, `map()`, `zip()` | `__iter__()` が自分自身を返す |
| **ジェネレータ** | `yield` で作られるイテレータ | `def gen(): yield ...` | 書きやすい使い切りのイテレータ |

## 3. 実例で見るイテラブルの使い方

### 3-1. シーケンス型イテラブル

```python
lst = ['a', 'b', 'c']
print(lst[0])        # a
for x in lst:
    print(x)         # a b c
```

文字列も同様に扱えます。

```python
text = "hello"
for ch in text:
    print(ch)
```

### 3-2. 非シーケンス型イテラブル

```python
s = {'apple', 'banana', 'cherry'}
for item in s:
    print(item)  # 順番は保証されない
```

辞書のキーもイテラブルです。

```python
d = {'a': 1, 'b': 2}
for key in d:
    print(key)
```

### 3-3. イテレータ

```python
lst = [10, 20, 30]
it = iter(lst)  # イテレータを作る
print(next(it))  # 10
print(next(it))  # 20
print(next(it))  # 30
# print(next(it))  # StopIterationエラー
```

### 3-4. ジェネレータ

```python
def countdown(n):
    while n > 0:
        yield n
        n -= 1

for num in countdown(3):
    print(num)  # 3 2 1
```

#### 補足：ジェネレータとは？

イテラブルの分類の1つである「ジェネレータ」は、`yield` を使って値を1つずつ返す**特殊な関数のようなもの**です。  
関数のように定義しますが、呼び出すと**「ジェネレータオブジェクト」というイテレータ**が返ってきます。

```python
def count_up(n):
    for i in range(n):
        yield i

gen = count_up(3)
print(gen)  # <generator object count_up at 0x...>
```

この `gen` はイテレータなので、`for` 文や `next()` を使って値を1つずつ取り出せます。

```python
print(next(gen))  # 0
print(next(gen))  # 1
print(next(gen))  # 2
```

✅ **ジェネレータの特徴（まとめ）**

- `yield` を使って定義する
- 呼び出すとジェネレータオブジェクト（イテレータ）が返る
- 使い切り（1回限り）
- メモリを節約できる
- for文や `next()` で使える

より詳しい内容は、別の記事にてまとめる予定です。

## 4. イテラブルとイテレータの関係

- **すべてのイテレータはイテラブル**
- **すべてのジェネレータはイテレータ**
- **シーケンスや集合はイテレータではないが、`iter()` でイテレータを作れる**

```python
s = {1, 2, 3}
it = iter(s)
print(next(it))  # 1（順不同）
```

## 5. 特徴比較表

| 分類 | 繰り返し可能 | 使い切り | 順序あり | インデックスアクセス |
|------|----------------|----------|-----------|----------------------|
| シーケンス型        | ✅        | ❌        | ✅        | ✅                    |
| 非シーケンス型      | ✅        | ❌        | ❌（dictはあり） | ❌                    |
| イテレータ          | ✅        | ✅        | ❌        | ❌                    |
| ジェネレータ        | ✅        | ✅        | ❌        | ❌                    |

## 6. イメージ図（文章による簡易図解）

```
イテラブル
├─ シーケンス型（list, tuple, str, range）
├─ 非シーケンス型（set, dict）
└─ イテレータ（__next__持つ）
    └─ ジェネレータ（yieldで作られたイテレータ）
```

## 7. 実務での使い分け

- **何度でも使いたいなら**：listやstrなどのシーケンス型が安心
- **順序を気にしない集合処理には**：setやdict（キー）を活用
- **1回だけのデータ処理には**：イテレータやジェネレータがメモリ効率的
- **大量データの逐次処理には**：ジェネレータ関数を定義して使うと便利

## おわりに

繰り返し処理できるオブジェクトといっても、Pythonにはその中に様々な違いがあります。  
「イテラブル」という共通の性質を持ちながら、使い方や特徴が異なる4つの分類を知ることで、  
コードを書くときの選択肢や理解が深まると思いました。

今後は、それぞれのパターンに出会ったときに「これはどのタイプのイテラブルか？」と意識してみたいです。