Pythonでの繰り返し処理(iterate)の基本と実用テクニック

python

Pythonでは、「リストを1つずつ処理する」「ファイルの行を読み取る」「辞書のキーと値を確認する」など、繰り返し処理(イテレート)はあらゆる場面で活用されます。

この記事では、Pythonにおけるiterate(繰り返し)の基本的な使い方から、for文、enumerate、リスト内包表記、ジェネレータなど、実務で役立つテクニックまで幅広く解説します。

スポンサーリンク

繰り返し処理(iterate)とは?

基本概念

「iterate」とは、リストやタプル、辞書などのデータを1つずつ取り出して処理することを指します。Pythonでは、forループを使ってこれを簡単に実現できます。

身近な例で考えてみよう:

  • 買い物リストを上から順番にチェックする
  • 本のページを1ページずつめくって読む
  • CDの曲を1曲ずつ再生する

これらと同じように、プログラムでもデータを順番に処理していきます。

基本的なfor文の使い方

# 果物のリストを1つずつ処理
fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
    print(fruit)

出力:

apple
banana
cherry

より実用的な例

# テストの点数を1つずつ確認
scores = [85, 92, 78, 96, 88]
total = 0

for score in scores:
    print(f"点数: {score}")
    total += score

average = total / len(scores)
print(f"合計: {total}点")
print(f"平均: {average:.1f}点")

出力:

点数: 85
点数: 92
点数: 78
点数: 96
点数: 88
合計: 439点
平均: 87.8点

文字列の繰り返し処理

# 文字列も1文字ずつ処理できる
word = "Python"
for char in word:
    print(f"文字: {char}")

出力:

文字: P
文字: y
文字: t
文字: h
文字: o
文字: n

この章のまとめ

基本のforループは、ほぼすべての反復処理に使えます。

次の章では、データの位置も一緒に扱いたい場合に便利なenumerateを紹介します。

indexも一緒に取りたいならenumerate

なぜenumerateが必要?

問題: リストの要素だけでなく、その位置(番号)も知りたい場合がある

例:こんな状況

  • ランキングを表示したい(1位、2位、3位…)
  • エラーが起きた行番号を知りたい
  • 配列の偶数番目だけ処理したい

enumerateの基本的な使い方

enumerate()は、リストの要素とそのインデックス(位置番号)を同時に取得できる関数です。

fruits = ['apple', 'banana', 'cherry']

for i, fruit in enumerate(fruits):
    print(f"{i}: {fruit}")

出力:

0: apple
1: banana
2: cherry

実用例1:ランキング表示

# テストの結果をランキング形式で表示
students = ['太郎', '花子', '次郎', '美咲']
scores = [92, 88, 95, 91]

# 点数でソート(高い順)
ranked_data = sorted(zip(students, scores), key=lambda x: x[1], reverse=True)

print("📊 テスト結果ランキング")
for rank, (name, score) in enumerate(ranked_data, start=1):
    print(f"{rank}位: {name}さん ({score}点)")

出力:

📊 テスト結果ランキング
1位: 次郎さん (95点)
2位: 太郎さん (92点)
3位: 美咲さん (91点)
4位: 花子さん (88点)

実用例2:CSVファイルの行番号付き処理

# ファイルの内容を行番号付きで表示
def read_file_with_line_numbers(filename):
    try:
        with open(filename, 'r', encoding='utf-8') as file:
            for line_num, line in enumerate(file, start=1):
                print(f"{line_num:3d}: {line.rstrip()}")
    except FileNotFoundError:
        print(f"ファイル '{filename}' が見つかりません")

# 使用例(実際のファイルがある場合)
# read_file_with_line_numbers('sample.txt')

実用例3:条件に合う位置の要素だけ処理

# 偶数番目の要素だけ大文字にする
words = ['hello', 'world', 'python', 'programming']
result = []

for i, word in enumerate(words):
    if i % 2 == 0:  # 偶数番目(0, 2, 4...)
        result.append(word.upper())
    else:
        result.append(word)

print("元のリスト:", words)
print("変更後:", result)

出力:

元のリスト: ['hello', 'world', 'python', 'programming']
変更後: ['HELLO', 'world', 'PYTHON', 'programming']

この章のまとめ

位置情報を使いたいときはenumerateが便利です。次は、繰り返しながら「新しいリストを作る」テクニック、リスト内包表記を紹介します。

リスト内包表記で効率的に繰り返す

リスト内包表記とは?

リスト内包表記(list comprehension)を使えば、1行で新しいリストを作ることができます。

従来の方法:

# 普通のfor文を使った場合
squares = []
for x in range(5):
    squares.append(x**2)
print(squares)  # [0, 1, 4, 9, 16]

リスト内包表記:

# 1行で同じことができる
squares = [x**2 for x in range(5)]
print(squares)  # [0, 1, 4, 9, 16]

基本的なパターン

パターン1:変換

# 文字列リストを大文字に変換
names = ['alice', 'bob', 'charlie']
upper_names = [name.upper() for name in names]
print(upper_names)  # ['ALICE', 'BOB', 'CHARLIE']

# 数値リストを2倍に
numbers = [1, 2, 3, 4, 5]
doubled = [num * 2 for num in numbers]
print(doubled)  # [2, 4, 6, 8, 10]

パターン2:条件付きフィルタリング

# 偶数だけを抽出
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = [num for num in numbers if num % 2 == 0]
print(even_numbers)  # [2, 4, 6, 8, 10]

# 特定の条件を満たす文字列だけ
words = ['apple', 'banana', 'cherry', 'date']
long_words = [word for word in words if len(word) > 5]
print(long_words)  # ['banana', 'cherry']

パターン3:条件付き変換

# 正の数はそのまま、負の数は0にする
numbers = [-2, -1, 0, 1, 2]
processed = [num if num > 0 else 0 for num in numbers]
print(processed)  # [0, 0, 0, 1, 2]

# 成績判定
scores = [85, 92, 78, 96, 88]
grades = ['A' if score >= 90 else 'B' if score >= 80 else 'C' for score in scores]
print(grades)  # ['B', 'A', 'C', 'A', 'B']

実用例1:データ変換

# 商品データの処理
products = [
    {'name': 'ノートPC', 'price': 80000},
    {'name': 'マウス', 'price': 2000},
    {'name': 'キーボード', 'price': 5000}
]

# 税込み価格を計算(税率10%)
tax_rate = 0.1
prices_with_tax = [
    {
        'name': item['name'],
        'price_without_tax': item['price'],
        'price_with_tax': int(item['price'] * (1 + tax_rate))
    }
    for item in products
]

for item in prices_with_tax:
    print(f"{item['name']}: {item['price_without_tax']}円 → {item['price_with_tax']}円")

出力:

ノートPC: 80000円 → 88000円
マウス: 2000円 → 2200円
キーボード: 5000円 → 5500円

実用例2:文字列処理

# ファイル名のリストから拡張子を抽出
filenames = ['document.pdf', 'image.jpg', 'data.csv', 'script.py']
extensions = [filename.split('.')[-1] for filename in filenames]
print(extensions)  # ['pdf', 'jpg', 'csv', 'py']

# メールアドレスからドメインを抽出
emails = ['user1@gmail.com', 'user2@yahoo.co.jp', 'user3@example.org']
domains = [email.split('@')[1] for email in emails]
print(domains)  # ['gmail.com', 'yahoo.co.jp', 'example.org']

ネストしたリスト内包表記

# 九九の表を作成
multiplication_table = [
    [i * j for j in range(1, 10)] 
    for i in range(1, 10)
]

# きれいに表示
for i, row in enumerate(multiplication_table, 1):
    print(f"{i}の段: {row}")

この章のまとめ

リストを加工しながら新しく作りたいときにはリスト内包表記が便利です。

次は、繰り返し処理の効率化に役立つ「ジェネレータ」について紹介します。

ジェネレータで効率的にiterate

ジェネレータとは?

ジェネレータは、大量のデータを少しずつ処理したいときに使う「省メモリ」な繰り返し手法です。

問題: 大きなリストを作ると、コンピュータのメモリ(RAM)をたくさん使ってしまう

解決: ジェネレータを使えば、必要な時に必要な分だけデータを作る

リストとジェネレータの比較

import sys

# 通常のリスト(メモリを大量消費)
big_list = [x for x in range(1000000)]
print(f"リストのメモリ使用量: {sys.getsizeof(big_list)} bytes")

# ジェネレータ(メモリ効率が良い)
big_generator = (x for x in range(1000000))
print(f"ジェネレータのメモリ使用量: {sys.getsizeof(big_generator)} bytes")

実行結果の例:

リストのメモリ使用量: 8448728 bytes
ジェネレータのメモリ使用量: 104 bytes

ジェネレータの作り方

方法1:ジェネレータ式

# リスト内包表記に似ているが、()を使う
squares_gen = (x**2 for x in range(5))

# 1つずつ取り出す
for square in squares_gen:
    print(square)

出力:

0
1
4
9
16

方法2:ジェネレータ関数(yield使用)

def count_up(n):
    """0からn-1まで数を数えるジェネレータ"""
    for i in range(n):
        print(f"今から{i}を返します")  # デバッグ用
        yield i

# 使用例
for num in count_up(3):
    print(f"受け取った数: {num}")

出力:

今から0を返します
受け取った数: 0
今から1を返します
受け取った数: 1
今から2を返します
受け取った数: 2

実用例1:ファイル処理

def read_large_file(filename):
    """大きなファイルを1行ずつ読むジェネレータ"""
    try:
        with open(filename, 'r', encoding='utf-8') as file:
            for line in file:
                yield line.strip()
    except FileNotFoundError:
        print(f"ファイル '{filename}' が見つかりません")
        return

# 使用例:大きなログファイルからエラー行だけ抽出
def find_error_lines(filename):
    error_count = 0
    for line_num, line in enumerate(read_large_file(filename), 1):
        if 'ERROR' in line:
            error_count += 1
            print(f"行 {line_num}: {line}")
            
            # 最初の10個のエラーだけ表示
            if error_count >= 10:
                break
    
    print(f"合計 {error_count} 個のエラーを見つけました")

# find_error_lines('app.log')  # 実際のファイルがある場合

実用例2:無限数列

def fibonacci():
    """フィボナッチ数列を無限に生成するジェネレータ"""
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# 最初の10個だけ表示
fib_gen = fibonacci()
for i in range(10):
    print(next(fib_gen), end=' ')
print()  # 改行

出力:

0 1 1 2 3 5 8 13 21 34 

実用例3:データ変換パイプライン

def read_numbers():
    """数値データを生成"""
    for i in range(10):
        yield i

def square_numbers(numbers):
    """数値を2乗する"""
    for num in numbers:
        yield num ** 2

def filter_even(numbers):
    """偶数だけを通す"""
    for num in numbers:
        if num % 2 == 0:
            yield num

# パイプラインを組み合わせ
pipeline = filter_even(square_numbers(read_numbers()))

print("偶数の2乗値:")
for result in pipeline:
    print(result, end=' ')
print()

出力:

偶数の2乗値:
0 4 16 36 64 

ジェネレータの注意点

# ジェネレータは一度しか使えない
gen = (x for x in range(3))

print("1回目:")
for num in gen:
    print(num)

print("2回目:")
for num in gen:  # 何も出力されない
    print(num)

print("2回目は何も出力されませんでした")

この章のまとめ

ジェネレータは大きなデータを扱う場面でとても有効です。

最後に、繰り返し可能(イテラブル)なオブジェクトの種類と判断方法を紹介します。

どんなオブジェクトがiterateできるのか?

イテラブル(iterable)とは?

イテラブル(iterable)とは、for文で繰り返せるオブジェクトのことです。

身近な例:

  • 本の章一覧(1章、2章、3章…)
  • 買い物リスト(りんご、バナナ、チェリー…)
  • 時間割(1時間目、2時間目、3時間目…)

代表的なイテラブルオブジェクト

1. リスト(list)

fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
    print(fruit)

2. タプル(tuple)

coordinates = (10, 20, 30)
for coord in coordinates:
    print(f"座標: {coord}")

3. 辞書(dict)

student_scores = {'太郎': 85, '花子': 92, '次郎': 78}

# キーだけ
for name in student_scores:
    print(f"名前: {name}")

# キーと値
for name, score in student_scores.items():
    print(f"{name}: {score}点")

# 値だけ
for score in student_scores.values():
    print(f"点数: {score}")

4. 集合(set)

unique_numbers = {1, 2, 3, 2, 1}  # 重複は自動で削除される
for num in unique_numbers:
    print(num)
# 出力: 1, 2, 3 (順序は保証されない)

5. 文字列(str)

word = "Python"
for char in word:
    print(f"文字: {char}")

6. range(範囲オブジェクト)

# 0から4まで
for i in range(5):
    print(i)

# 1から10まで、2つずつ
for i in range(1, 11, 2):
    print(i)  # 1, 3, 5, 7, 9

イテラブルかどうかの判定方法

from collections.abc import Iterable

# いろいろなオブジェクトをテスト
test_objects = [
    [1, 2, 3],           # リスト
    (1, 2, 3),           # タプル
    {'a': 1, 'b': 2},    # 辞書
    {1, 2, 3},           # 集合
    "hello",             # 文字列
    range(5),            # range
    123,                 # 整数
    12.34,               # 浮動小数点数
    True                 # ブール値
]

for obj in test_objects:
    is_iterable = isinstance(obj, Iterable)
    print(f"{obj} ({type(obj).__name__}): {'✓' if is_iterable else '✗'}")

出力:

[1, 2, 3] (list): ✓
(1, 2, 3) (tuple): ✓
{'a': 1, 'b': 2} (dict): ✓
{1, 2, 3} (set): ✓
hello (str): ✓
range(0, 5) (range): ✓
123 (int): ✗
12.34 (float): ✗
True (bool): ✗

実用例:安全な繰り返し処理

def safe_iterate(obj):
    """オブジェクトが繰り返し可能かチェックしてから処理"""
    if isinstance(obj, Iterable):
        print(f"{obj} は繰り返し可能です:")
        for item in obj:
            print(f"  - {item}")
    else:
        print(f"{obj} は繰り返しできません({type(obj).__name__}型)")

# テスト
test_data = [
    [1, 2, 3],
    "hello",
    42,
    {'name': '太郎', 'age': 20}
]

for data in test_data:
    safe_iterate(data)
    print()  # 空行

カスタムイテラブルの作成

class CountDown:
    """カウントダウンするカスタムイテラブル"""
    
    def __init__(self, start):
        self.start = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.start <= 0:
            raise StopIteration
        
        self.start -= 1
        return self.start + 1

# 使用例
countdown = CountDown(5)
for num in countdown:
    print(f"カウントダウン: {num}")

出力:

カウントダウン: 5
カウントダウン: 4
カウントダウン: 3
カウントダウン: 2
カウントダウン: 1

応用テクニック

zip()で複数のイテラブルを同時処理

names = ['太郎', '花子', '次郎']
ages = [20, 22, 19]
scores = [85, 92, 78]

# 3つのリストを同時に処理
for name, age, score in zip(names, ages, scores):
    print(f"{name}({age}歳): {score}点")

出力:

太郎(20歳): 85点
花子(22歳): 92点
次郎(19歳): 78点

itertools モジュールの活用

import itertools

# 無限に繰り返す
colors = ['red', 'green', 'blue']
color_cycle = itertools.cycle(colors)

for i, color in enumerate(color_cycle):
    print(f"{i+1}: {color}")
    if i >= 7:  # 8回で停止
        break

出力:

1: red
2: green
3: blue
4: red
5: green
6: blue
7: red
8: green

パフォーマンスの比較

import time

# 大きなデータでの処理時間比較
def time_comparison():
    n = 1000000
    
    # リスト内包表記
    start = time.time()
    squares_list = [x**2 for x in range(n)]
    sum_list = sum(squares_list)
    list_time = time.time() - start
    
    # ジェネレータ
    start = time.time()
    squares_gen = (x**2 for x in range(n))
    sum_gen = sum(squares_gen)
    gen_time = time.time() - start
    
    print(f"リスト方式: {list_time:.3f}秒")
    print(f"ジェネレータ方式: {gen_time:.3f}秒")
    print(f"結果は同じ: {sum_list == sum_gen}")

# time_comparison()  # 実行すると時間を測定

まとめ

重要ポイント

Pythonでのiterate(繰り返し処理)は、日常的に使う非常に重要なテクニックです。今回のまとめ:

技法用途メリット
基本のfor文基本的な繰り返しシンプルで読みやすい
enumerate位置も知りたい時インデックスと値を同時取得
リスト内包表記新しいリストを作成1行で簡潔に書ける
ジェネレータ大量データの処理メモリ効率が良い

使い分けのガイド

小さなデータ(1000個未満)

# リスト内包表記がおすすめ
result = [process(item) for item in data]

大きなデータ(100万個以上)

# ジェネレータがおすすめ
result = (process(item) for item in data)

位置情報が必要

# enumerateを使用
for i, item in enumerate(data):
    print(f"{i}: {item}")

コメント

タイトルとURLをコピーしました