結論から言うと、単発の処理をまとめたいときは関数、状態を持つ対象を扱いたいときはクラスが向いています。

Pythonを学んでいると、「クラスを覚えたから何でもクラスにしたほうがよいのか」と迷うことがあります。
ですが、実際には関数で十分な場面も多く、無理にクラスへ寄せるとコードが重くなることもあります。
この記事では、クラスと関数をどう使い分けるかを、動くコードと一緒に整理します。

この記事でわかること

  • クラスと関数の基本的な役割の違い
  • どんな場面で関数だけで十分なのか
  • 状態を持つならクラスが向いている理由

完成コード

完成コードは次の通りです。

def calculate_discounted_price(price, discount_rate):
    return int(price * (1 - discount_rate))


class Coupon:
    def __init__(self, code, discount_rate, remaining_uses):
        self.code = code
        self.discount_rate = discount_rate
        self.remaining_uses = remaining_uses

    def apply_to(self, price):
        if self.remaining_uses <= 0:
            raise ValueError("このクーポンはもう使えません。")
        self.remaining_uses -= 1
        return calculate_discounted_price(price, self.discount_rate)


print(calculate_discounted_price(5000, 0.2))

coupon = Coupon("SPRING20", 0.2, 2)
print(coupon.apply_to(5000))
print(coupon.remaining_uses)

コードのポイント

このコードでは、単純な割引計算は関数にし、使用回数を持つクーポンはクラスにしています。

  • calculate_discounted_price() は、引数だけで完結する計算です
  • Coupon は、割引率や残り使用回数という状態を持ちます
  • apply_to() は、その状態を使いながら処理を行います

同じ「割引」に関するコードでも、状態を持つかどうかで向いている形が変わります。

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

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

1. 入力から結果を返すだけなら関数が向いています

def calculate_discounted_price(price, discount_rate):
    return int(price * (1 - discount_rate))

この関数は、価格と割引率を受け取り、割引後の価格を返すだけです。
前回の結果を覚える必要もなく、内部に状態を持つ必要もありません。

このように、引数を受け取って結果を返すだけの処理は、関数のほうがシンプルです。
無理にクラスへ入れなくても、十分に読みやすく保てます。

2. 状態を持つ対象はクラスにすると整理しやすくなります

class Coupon:
    def __init__(self, code, discount_rate, remaining_uses):
        self.code = code
        self.discount_rate = discount_rate
        self.remaining_uses = remaining_uses

クーポンには、コード、割引率、残り使用回数のような情報があります。
こうした関連する値をまとめて扱いたいときは、クラスが向いています。

もし関数だけで扱うなら、毎回 code discount_rate remaining_uses を別々に渡す必要があり、更新も追いづらくなります。
状態を持つ対象はクラスにまとめると、何を扱っているかが明確になります。

3. 状態を読み書きする処理はクラスのメソッドに置くと自然です

    def apply_to(self, price):
        if self.remaining_uses <= 0:
            raise ValueError("このクーポンはもう使えません。")
        self.remaining_uses -= 1
        return calculate_discounted_price(price, self.discount_rate)

このメソッドは、割引を適用するだけでなく、残り使用回数も減らしています。
つまり、クーポンの状態を読み取り、更新する処理です。

このような処理は、関数よりクラスのメソッドにしたほうが自然です。
対象の状態と処理が近い場所にあるため、あとから読んでも流れを追いやすくなります。

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

実務では、「その処理は前回の状態を覚える必要があるか」で考えると、クラスと関数を分けやすいです。

  • 単純な変換や計算は、関数のままのほうが軽くて読みやすいです
  • 複数の関連データをひとまとまりで扱うなら、クラスが向いています
  • 状態更新がある処理は、クラスに寄せたほうが変更箇所を追いやすくなります
  • 迷ったら、まず関数で考えて、状態を持つ必要が出てきたらクラスを検討すると整理しやすいです

たとえばCLIツールや業務スクリプトでも、文字列整形や集計は関数で十分です。
一方で、設定、接続情報、複数回使う処理対象などを持つなら、クラスにしたほうが保守しやすくなります。

よくある勘違い・注意点

  • クラスのほうが高度だからといって、常にクラスを選ぶ必要はありません
  • 関数だけで十分な処理をクラスにすると、かえって読みづらくなることがあります
  • 逆に、状態を持つ対象を全部バラバラの関数で扱うと、更新の流れを追いにくくなります
  • 大切なのは文法の難しさではなく、処理の性質に合った形を選ぶことです

まとめ

  • 単発の計算や変換は関数、状態を持つ対象はクラスが向いています
  • 関数はシンプルな処理を軽く書けるのが強みです
  • クラスは関連データと状態更新をまとめて扱えるのが強みです
  • 実務では、状態を持つ必要があるかを基準に考えると判断しやすいです