Python リスト内包表記を使った業務効率化パターン集 | 実務コード解説

Python

Python リスト内包表記を使った業務効率化パターン集

Pythonを使った業務システム開発やデータ処理を行っていると、毎日のようにリスト内包表記のお世話になります。本記事では、単なる文法解説ではなく、実務で実際に遭遇するユースケースを中心に、リスト内包表記の活用パターンを紹介していきます。

リスト内包表記の簡易解説

リスト内包表記とは、既存のリストから新しいリストを生成する簡潔な記法です。通常のfor文とif文をコンパクトに書き直す方法として用いられます。

基本的な形式は以下の通りです:

# 基本形式
[式 for 変数 in イテラブル]

# 条件付き
[式 for 変数 in イテラブル if 条件]

# ネストした場合
[式 for 変数1 in イテラブル1 for 変数2 in イテラブル2]

これを従来のfor文で書くと、複数行になる処理が1行で完結するため、コード量の削減と可読性の向上が期待できます。

業務でのリスト内包表記のユースケース

実務では、リスト内包表記が活躍するシーンは多岐にわたります。データベースから取得した結果の加工、CSVファイルの処理、APIレスポンスの整形など、日々のデータ処理業務で頻繁に登場します。

特に以下のようなケースが多いです:

  • 複数のデータソースから特定の情報を抽出する
  • データ型の変換や正規化を行う
  • 条件に基づいてデータをフィルタリングする
  • 複数のデータを組み合わせて新しい構造を作成する
  • 既存データの値を一括で変換する

実装コード:実務パターン5選

パターン1:販売データの売上金額を一括計算

ECサイトの販売管理システムでよく見かけるパターンです。各商品の数量と単価から売上金額を計算します。

# 従来のfor文での実装
sales_data = [
    {'product': 'りんご', 'quantity': 10, 'price': 150},
    {'product': 'みかん', 'quantity': 20, 'price': 100},
    {'product': 'バナナ', 'quantity': 15, 'price': 80},
]

# for文版
results = []
for item in sales_data:
    revenue = item['quantity'] * item['price']
    results.append({
        'product': item['product'],
        'revenue': revenue
    })

# リスト内包表記版(こちらを使用)
results = [
    {
        'product': item['product'],
        'revenue': item['quantity'] * item['price']
    }
    for item in sales_data
]

print(results)
# 出力: [{'product': 'りんご', 'revenue': 1500}, 
#        {'product': 'みかん', 'revenue': 2000}, 
#        {'product': 'バナナ', 'revenue': 1200}]

パターン2:ユーザーログから特定の条件でフィルタリング

ログ分析業務では、大量のデータから特定条件のレコードを抽出することが日常です。このパターンはそうした場面でよく使われます。

# ユーザーアクセスログの例
access_logs = [
    {'user_id': 101, 'action': 'login', 'status_code': 200, 'timestamp': '2024-01-15 10:30'},
    {'user_id': 102, 'action': 'purchase', 'status_code': 200, 'timestamp': '2024-01-15 10:35'},
    {'user_id': 103, 'action': 'login', 'status_code': 401, 'timestamp': '2024-01-15 10:40'},
    {'user_id': 101, 'action': 'logout', 'status_code': 200, 'timestamp': '2024-01-15 10:50'},
    {'user_id': 104, 'action': 'purchase', 'status_code': 500, 'timestamp': '2024-01-15 10:55'},
]

# ステータスコードが200(成功)で、アクション名がpurchaseのレコードを抽出
successful_purchases = [
    log for log in access_logs 
    if log['status_code'] == 200 and log['action'] == 'purchase'
]

print(successful_purchases)
# 出力: [{'user_id': 102, 'action': 'purchase', 'status_code': 200, 'timestamp': '2024-01-15 10:35'}]

パターン3:CSVからの読み込みデータを正規化

CSVファイルから読み込んだデータは文字列型で、多くの場合で型変換や空白文字の除去が必要になります。

import csv
from io import StringIO

# CSVデータの例(実際はファイルから読み込む)
csv_data = \"\"\"customer_id,name,age,purchase_amount
1001, 田中太郎 ,28,15000
1002, 鈴木花子 ,35,22500
1003, 佐藤次郎 ,42,8900
\"\"\"

# CSVをパース
csv_reader = csv.DictReader(StringIO(csv_data))

# データを正規化(空白削除、型変換)
normalized_data = [
    {
        'customer_id': int(row['customer_id']),
        'name': row['name'].strip(),
        'age': int(row['age']),
        'purchase_amount': int(row['purchase_amount'])
    }
    for row in csv_reader
]

print(normalized_data)
# 出力: [
#   {'customer_id': 1001, 'name': '田中太郎', 'age': 28, 'purchase_amount': 15000},
#   {'customer_id': 1002, 'name': '鈴木花子', 'age': 35, 'purchase_amount': 22500},
#   {'customer_id': 1003, 'name': '佐藤次郎', 'age': 42, 'purchase_amount': 8900}
# ]

パターン4:複数のリストをマージして新しい構造を作成

異なるデータソースから取得した情報を組み合わせることは、データ統合業務では頻繁です。

# 商品マスタデータ
products = [
    {'id': 'P001', 'name': 'ノートPC'},
    {'id': 'P002', 'name': 'マウス'},
    {'id': 'P003', 'name': 'キーボード'},
]

# 在庫データ
inventory = [
    {'product_id': 'P001', 'stock': 50},
    {'product_id': 'P002', 'stock': 200},
    {'product_id': 'P003', 'stock': 150},
]

# 在庫辞書を作成(検索を高速化)
inventory_dict = {item['product_id']: item['stock'] for item in inventory}

# マージして新しい構造を作成
product_with_stock = [
    {
        'id': product['id'],
        'name': product['name'],
        'stock': inventory_dict.get(product['id'], 0)
    }
    for product in products
]

print(product_with_stock)
# 出力: [
#   {'id': 'P001', 'name': 'ノートPC', 'stock': 50},
#   {'id': 'P002', 'name': 'マウス', 'stock': 200},
#   {'id': 'P003', 'name': 'キーボード', 'stock': 150}
# ]

パターン5:ネストされたデータ構造を平坦化

複数回答形式のアンケート結果やツリー構造のデータを扱う際に、平坦化が必要になることがあります。

# 部門ごとの従業員リスト
departments = [
    {
        'dept_name': '営業部',
        'employees': [
            {'id': 1, 'name': '山田太郎'},
            {'id': 2, 'name': '佐藤花子'}
        ]
    },
    {
        'dept_name': 'エンジニア部',
        'employees': [
            {'id': 3, 'name': '鈴木次郎'},
            {'id': 4, 'name': '田中美咲'}
        ]
    }
]

# ネストされたリストを平坦化しながら部門情報を保持
flat_employees = [
    {
        'dept_name': dept['dept_name'],
        'id': emp['id'],
        'name': emp['name']
    }
    for dept in departments
    for emp in dept['employees']
]

print(flat_employees)
# 出力: [
#   {'dept_name': '営業部', 'id': 1, 'name': '山田太郎'},
#   {'dept_name': '営業部', 'id': 2, 'name': '佐藤花子'},
#   {'dept_name': 'エンジニア部', 'id': 3, 'name': '鈴木次郎'},
#   {'dept_name': 'エンジニア部', 'id': 4, 'name': '田中美咲'}
# ]

よくある応用パターン

条件分岐を含むリスト内包表記

単純なフィルタリングだけでなく、条件に応じて異なる値を設定したいケースがあります。

# 売上実績に応じてボーナス倍率を決定する例
employee_sales = [
    {'name': '田中', 'sales': 5000000},
    {'name': '佐藤', 'sales': 3000000},
    {'name': '鈴木', 'sales': 7000000},
]

# 条件分岐付きリスト内包表記
bonus_rates = [
    {
        'name': emp['name'],
        'sales': emp['sales'],
        'bonus_rate': 0.05 if emp['sales'] >= 5000000 else 0.02
    }
    for emp in employee_sales
]

print(bonus_rates)
# 出力: [
#   {'name': '田中', 'sales': 5000000, 'bonus_rate': 0.05},
#   {'name': '佐藤', 'sales': 3000000, 'bonus_rate': 0.02},
#   {'name': '鈴木', 'sales': 7000000, 'bonus_rate': 0.05}
# ]

辞書内包表記の活用

リスト内包表記だけでなく、辞書内包表記も業務では頻繁に使われます。

# 顧客リストから顧客IDをキーとした辞書を作成
customers = [
    {'id': 'C001', 'name': '佐藤商事', 'credit_limit': 5000000},
    {'id': 'C002', 'name': '田中製造', 'credit_limit': 3000000},
    {'id': 'C003', 'name': '鈴木物流', 'credit_limit': 8000000},
]

# 辞書内包表記で高速検索可能な構造に変換
customer_dict = {
    customer['id']: {
        'name': customer['name'],
        'credit_limit': customer['credit_limit']
    }
    for customer in customers
}

# 高速な検索が可能
print(customer_dict['C002'])
# 出力: {'name': '田中製造', 'credit_limit': 3000000}

文字列操作を含むリスト内包表記

テキストデータの加工はデータ処理の中でも重要です。

# メールアドレスリストをドメイン別に整理
emails = [
    'tanaka@example.com',
    'suzuki@example.co.jp',
    'satoh@another.com',
    'yamada@example.co.jp',
]

# ドメイン抽出
domains = [email.split('@')[1] for email in emails]
print(domains)
# 出力: ['example.com', 'example.co.jp', 'another.com', 'example.co.jp']

# ドメイン別に整理した辞書を作成
domains_dict = {
    domain: [email for email in emails if email.endswith(f'@{domain}')]
    for domain in set(domains)
}
print(domains_dict)
# 出力: {
#   'another.com': ['satoh@another.com'],
#   'example.co.jp': ['suzuki@example.co.jp', 'yamada@example.co.jp'],
#   'example.com': ['tanaka@example.com']
# }

リスト内包表記使用時の注意点

可読性の低下に注意

複雑な条件やネストが深くなると、むしろ可読性が低下します。以下の例を見てください:

# 悪い例:複雑すぎて何をしているか理解しづらい
result = [
    x[0] if len(x[0]) > 5 else x[1] 
    for x in [
        [item.strip().upper(), item.lower()] 
        for item in data 
        if item and isinstance(item, str)
    ]
]

# 良い例:for文の方が分かりやすい
result = []
for item in data:
    if not item or not isinstance(item, str):
        continue
    
    cleaned = item.strip().upper()
    if len(cleaned) > 5:
        result.append(cleaned)
    else:
        result.append(item.lower())

リスト内包表記は便利ですが、可読性とのバランスを考慮して使い分けることが重要です。

パフォーマンスへの配慮

大規模データセットを扱う場合は、パフォーマンスも考慮が必要です。

# 大量の辞書をループして値を検索する場合
# 非効率な例:O(n²)の計算量
ids_to_find = [1001, 1002, 1003, 1004, 1005]
customers = [{'id': i, 'name': f'Customer{i}'} for i in range(1, 10001)]

# この方法は遅い
result_slow = [
    customer 
    for cust_id in ids_to_find 
    for customer in customers 
    if customer['id'] == cust_id
]

# 改善:辞書を先に作成してから検索(O(n))
customer_dict = {customer['id']: customer for customer in customers}
result_fast = [customer_dict[cust_id] for cust_id in ids_to_find]

副作用を伴う処理は避ける

リスト内包表記内で外部状態を変更するような副作用のある処理は避けるべきです。

# 悪い例:副作用がある
processed = []
result = [
    processed.append(item) or item * 2  # 副作用あり
    for item in [1, 2, 3, 4, 5]
]

# 良い例:通常のfor文で行う
processed = []
result = []
for item in [1, 2, 3, 4, 5]:
    processed.append(item)
    result.append(item * 2)

実務での活用シーン別チェックリスト

リスト内包表記を使うかどうか判断する際のチェックリストです:

  • 使った方が良い場合:
    ✓ 単純なフィルタリング
    ✓ 型変換や正規化
    ✓ 1~2段階のネスト
    ✓ チーム内で一般的な処理
    ✓ 処理が1~2行で完結する
  • for文を使った方が良い場合:
    ✗ 3段階以上のネスト
    ✗ 複数の条件分岐
    ✗ 副作用を伴う処理
    ✗ デバッグが必要な複雑な処理
    ✗ チーム内でまだ習熟度が低い

まとめ

Pythonのリスト内包表記は、正しく使うことで業務コードを効率的かつ読みやすくできる強力なツールです。本記事で紹介した5つのパターンは、実務でほぼ毎日のように遭遇するケースばかりです。

重要なポイントをまとめます:

  • 基本:リスト内包表記は、単純なfor文とif文の置き換えとしてスタートする
  • 実務:データ加工・フィルタリング・型変換が主な用途
  • 応用:条件分岐や辞書内包表記へと拡張可能
  • 注意:可読性とパフォーマンスのバランスを常に考慮する
  • 使い分け:複雑すぎる場合は素直にfor文に戻す勇気も大切

チーム開発では、可読性も重要です。リスト内包表記は便利ですが、チームメンバーが理解できることを前提に、適切に使い分けることが実務での成功につながります。

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