結論から言うと、処理の役割が変わる場所、名前を付けられる場所、入力と出力を分けて考えられる場所は、関数に切り出す候補です。

Pythonを書いていると、ひとまず1つの場所にまとめて動かし、そのあとで「このままだと読みにくい」と感じることがあります。
ただ、どこで関数に分ければよいかが曖昧だと、細かく分けすぎたり、逆に長いまま放置したりしやすいです。
この記事では、処理を関数に切り出す判断基準を、実際に動くコードを見ながら整理します。

この記事でわかること

  • 処理を関数に切り出すかどうかを判断する基準
  • 長い処理を分けるときに見るべき役割の違い
  • 関数を増やしすぎないための考え方

完成コード

最初に必ず動くPythonコードを1つ示します。

def validate_order(order):
    required_keys = ["customer_name", "items", "is_member"]
    for key in required_keys:
        if key not in order:
            raise ValueError(f"{key} が不足しています。")

    if not order["items"]:
        raise ValueError("items は1件以上必要です。")


def calculate_total(items, is_member):
    subtotal = sum(item["price"] * item["quantity"] for item in items)
    shipping_fee = 0 if subtotal >= 5000 else 500
    discount = int(subtotal * 0.1) if is_member else 0
    return subtotal + shipping_fee - discount


def build_message(customer_name, total):
    return f"{customer_name}さんのご注文金額は {total} 円です。"


def process_order(order):
    validate_order(order)
    total = calculate_total(order["items"], order["is_member"])
    return build_message(order["customer_name"], total)


sample_order = {
    "customer_name": "佐藤",
    "items": [
        {"name": "USBケーブル", "price": 1200, "quantity": 2},
        {"name": "キーボード", "price": 3200, "quantity": 1},
    ],
    "is_member": True,
}

print(process_order(sample_order))

コードのポイント

このコードでは、注文処理を1つの大きな流れとして持ちながら、その中を役割ごとに分けています。

  • 入力チェックは validate_order()
  • 金額計算は calculate_total()
  • 表示用メッセージの作成は build_message()
  • 全体の流れをまとめるのは process_order()

このように、役割が変わる場所で関数を切り出すと、読む側が処理を追いやすくなります。

コードを順番に説明します

主要な処理を分けて説明します。

1. 入力チェックという独立した役割を切り出します

def validate_order(order):
    required_keys = ["customer_name", "items", "is_member"]
    for key in required_keys:
        if key not in order:
            raise ValueError(f"{key} が不足しています。")

    if not order["items"]:
        raise ValueError("items は1件以上必要です。")

この部分は、注文データが処理できる形になっているかを確認する役割です。
計算や表示とは目的が違うので、別の関数にすると意味がはっきりします。

ここでの判断基準は、処理の責務が変わっているかどうかです。
「チェックする処理」と「計算する処理」は性質が違うため、分ける価値があります。

2. 計算ルールを1か所にまとめます

def calculate_total(items, is_member):
    subtotal = sum(item["price"] * item["quantity"] for item in items)
    shipping_fee = 0 if subtotal >= 5000 else 500
    discount = int(subtotal * 0.1) if is_member else 0
    return subtotal + shipping_fee - discount

この関数では、合計金額を求めることだけに集中しています。
送料や会員割引のように、あとから変更されやすい業務ルールは、関数に閉じ込めると修正しやすくなります。

ここでの判断基準は、ひとかたまりに名前を付けられるかどうかです。
この処理には「合計金額を計算する」という自然な名前を付けられるので、関数として独立させやすいです。

3. 表示用の整形を計算処理から分けます

def build_message(customer_name, total):
    return f"{customer_name}さんのご注文金額は {total} 円です。"


def process_order(order):
    validate_order(order)
    total = calculate_total(order["items"], order["is_member"])
    return build_message(order["customer_name"], total)

表示メッセージの作成は、金額計算そのものとは別の役割です。
この2つを分けておくと、あとで「画面表示用」と「ログ保存用」で文面を変えたいときにも対応しやすくなります。

ここでの判断基準は、入力と出力が整理できるかどうかです。
calculate_total() は数値を返し、build_message() は文字列を返しています。
返す値の種類が変わる場所は、関数を分ける候補になりやすいです。

実務で使うときのポイント

実務では、次の観点で関数に切り出すかどうかを考えると判断しやすいです。

  • 処理の目的が変わるなら分けます。チェック、計算、保存、表示は同じ関数に詰め込みすぎないほうが保守しやすいです。
  • ひとことで説明できる処理は、関数名にしやすいため切り出しやすいです。逆に名前が曖昧なら、まだ責務が混ざっている可能性があります。
  • テストしたい単位で分けるのも有効です。送料計算だけ確かめたいなら、計算処理が独立しているほうが確認しやすいです。
  • ただし、数行ごとに無理に分ける必要はありません。短くても役割が1つなら、そのままのほうが読みやすいこともあります。

「同じ処理を再利用するから関数にする」という考え方はよく出てきますが、再利用だけが基準ではありません。
1回しか使わなくても、長くて責務が混ざっている処理は、関数に分ける価値があります。

よくある勘違い・注意点

  • 関数を細かく分けすぎると、処理の流れが逆に追いにくくなることがあります
  • 関数名が do_taskprocess_data のように曖昧だと、何をしているのか伝わりにくくなります
  • 引数を増やしすぎると、分割したのに扱いづらい関数になりやすいです
  • 計算と print() を同じ関数に入れると、あとで再利用しにくくなることがあります

まとめ

  • 処理の役割が変わる場所は、関数に切り出す候補です
  • 自然な名前を付けられる処理は、独立した関数にしやすいです
  • 入力と出力の種類が切り替わる場所も、分割の目安になります
  • 再利用の有無だけでなく、読みやすさと修正しやすさでも判断できます

前の記事

次の記事