Pythonのクラス(class)を理解しよう!初心者のためのPythonクラス徹底解説

Pythonのクラス(class)を理解しよう!初心者のためのPythonクラス講座
  • URLをコピーしました!

当ブログのコンテンツ・情報について、できる限り正確な情報を提供するように努めておりますが、正確性や安全性を保証するものではありません。
当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますので、予めご了承ください。

Pythonはそのシンプルさと柔軟性から、初心者にも優しいプログラミング言語として知られています。

特に、Pythonはオブジェクト指向を採用しており、オブジェクトを使った設計が簡単にできる点が大きな特徴です。その中でも、「class(クラス)」という概念は、Pythonをより深く理解し、効果的に活用するために欠かせません。

この記事では、Pythonのクラスの基本から実際の使用例までを解説し、初心者が自信を持ってクラスを使いこなせるようになることを目指します。

目次

オブジェクト指向とは?

まずは、オブジェクト指向の基本概念から解説します。

オブジェクト指向とは、プログラムをオブジェクトの集合として捉える手法です。オブジェクトとは、データとそのデータを操作するためのメソッドを一つにまとめたものです。

データを「部品」、メソッドを「部品の組み立て方」とするならば、オブジェクトはそれらを組み合わせて作られる「製品」と考えることができます。そして、製品の「設計図」がクラス、「実際に製造された製品」をインスタンスと呼びます。

Python用語 例え
データ部品
メソッド部品の組み立て方
オブジェクト製品
クラス設計図(部品の種類、組み立て方、組み立て方法)
インスタンス実際の製品

Pythonでクラスを扱う方法

クラスの定義

クラスとは製品(オブジェクト)を製造するための「設計図」です。設計図には、部品の情報の他にも、部品をどのように組み立てるかといった操作手順が記されています。

同様に、クラスもそのオブジェクトが持つデータや、データの種類、そのデータをどのように操作するかを定義します。

以下は、具体的なクラスの定義例です。このDataContainerクラスは、2つのデータabを持ち、データに値をセットするメソッドsetと、これらのデータの合計を計算して返すメソッドsumを持っています。selfはクラス内のメソッドが自身を指すための引数です。

# DataContainerクラスを定義
class DataContainer:
    a = 0  # データaを設定(初期値として0を代入)
    b = 0  # データbを設定(初期値として0を代入)

    # aとbに新しい値をセットするメソッド
    def set(self, a, b):
        self.a = a  # 新しい値aを代入
        self.b = b  # 新しい値bを代入

    # aとbの合計を計算して返すメソッド
    def sum(self):
        return self.a + self.b

インスタンス

インスタンスとは、クラス(設計図)をもとに生成したオブジェクト(実際の製品)のことです。インスタンス化によってクラスに定義したデータやメソッドを持つ実際のオブジェクトが作成されます。

以下はのコードは、定義したDataContainerクラスのインスタンスを生成し、そのインスタンスを通じてsetメソッドとsumメソッドを呼び出し、最終的な結果をコンソールに表示させています。

# DataContainerクラスを定義
class DataContainer:
    a = 0  # データaを設定(初期値として0を代入)
    b = 0  # データbを設定(初期値として0を代入)

    # aとbの合計を計算して返すメソッド
    def sum(self):
        return self.a + self.b

    # aとbに新しい値をセットするメソッド
    def set(self, a, b):
        self.a = a  # 新しい値aを代入
        self.b = b  # 新しい値bを代入

# DataContainerクラスのインスタンスを生成、変数containerに代入
container = DataContainer()

# インスタンスを通じてsetメソッドを呼び出し、データa,bに新しい値を代入
container.set(5, 3)

# インスタンスを通じてsumメソッドを呼び出し、aとbの合計を計算
result_sum = container.sum()

# 計算結果をコンソールに表示
print(result_sum) # 出力結果: 8

このようにインスタンスを通じて、クラス定義を基にした個別のオブジェクトが具体的な操作を可能にします。

コンストラクタ

コンストラクタは、インスタンスが生成される際に自動的に呼ばれる特別なメソッドです。Pythonでは__init__という名前のメソッドがコンストラクタとして機能します。このメソッドを使って、クラスのインスタンスが生成されるときに初期設定を行うことができます。

例えば、先ほどのコードのDataContainerクラスにコンストラクタを追加して、初期値を設定することができます。

以下の例では、DataContainerクラスのインスタンスが生成されたときに、abに初期値を設定しています。結果として、setメソッドを削除することができ、コードを簡素化することができます

# DataContainerクラスを定義
class DataContainer:
    # コンストラクタでaとbを初期化
    def __init__(self, a=0, b=0):
        self.a = a  # 初期値としてaを代入
        self.b = b  # 初期値としてbを代入

    # aとbの合計を計算して返すメソッド
    def sum(self):
        return self.a + self.b

# DataContainerクラスのインスタンスを生成し、a=5, b=3として初期化
container = DataContainer(5, 3)

# sumメソッドを呼び出し、aとbの合計を計算
result_sum = container.sum()

# 計算結果をコンソールに表示
print(result_sum)  # 出力結果: 8

デストラクタ

デストラクタは、オブジェクトが不要になった際に自動的に呼ばれるメソッドで、Pythonでは__del__という名前のメソッドがデストラクタとして機能します。

先ほどのコードのDataContainerクラスにデストラクタを追加すると下記のようになります。

# DataContainerクラスを定義
class DataContainer:
    # コンストラクタでaとbを初期化
    def __init__(self, a=0, b=0):
        self.a = a  # 初期値としてaを代入
        self.b = b  # 初期値としてbを代入

    # aとbの合計を計算して返すメソッド
    def sum(self):
        return self.a + self.b

    # デストラクタを定義
    def __del__(self):
        # インスタンスが削除されるときに実行される
        print(f"DataContainer with a={self.a}, b={self.b} is being deleted.")

# DataContainerクラスのインスタンスを生成し、a=5, b=3として初期化
container = DataContainer(5, 3)

# sumメソッドを呼び出し、aとbの合計を計算
result_sum = container.sum()

# 計算結果をコンソールに表示
print(result_sum)  # 出力結果: 8

# ここでDataContainerクラスが削除されるタイミングでデストラクタが呼ばれる

ただし、Pythonではメモリ管理が自動で行われるためオブジェクトが使われなくなるとリソースは自動的に解放します。そのため、デストラクタで明示的にリソースの解放を行わなくても、通常は問題ありません。

しかし、外部リソース(ファイル操作、ネットワーク接続、データベース接続など)を扱っている場合は、デストラクタで適切にクリーンアップを行わないと、リソースの浪費、さらにはプログラムの不安定な動作を引き起こす可能性があります。

クラスの継承

クラスの継承は、あるクラスが持つ特性(データとメソッド)を別のクラスに引き継ぐことです。継承を利用すると既存のコードの再利用が容易になり、コードの管理が簡単になります。

例として、DataContainerクラスから派生したExtendedDataContainerクラスを定義します。この新しいクラスは、基本的な機能はそのままに、追加のメソッドを持つことができます。

# DataContainerクラスを定義
class DataContainer:
    # コンストラクタでaとbを初期化
    def __init__(self, a=0, b=0):
        self.a = a  # 初期値としてaを代入
        self.b = b  # 初期値としてbを代入

    # aとbの合計を計算して返すメソッド
    def sum(self):
        return self.a + self.b

# DataContainerクラスのインスタンスを生成し、a=5, b=3として初期化
container = DataContainer(5, 3)

# sumメソッドを呼び出し、aとbの合計を計算
result_sum= container.sum()

# 計算結果をコンソールに表示
print(result_sum)  # 出力結果: 8

# DataContainerクラスを継承した新しいExtendedDataContainerクラスを定義
class ExtendedDataContainer(DataContainer):
    # aとbの乗算を計算して返すメソッド
    def multiply(self):
        return self.a * self.b

# ExtendedDataContainerクラスのインスタンスを生成し、a=5, b=3として初期化
container = ExtendedDataContainer(5, 3)

# multiplyメソッドを呼び出し、aとbの乗算を計算
result_multiply = container.multiply()

# 計算結果をコンソールに表示
print(result_multiply)  # 出力結果: 15

これらの概念を理解し、適切に活用することで、Pythonでのプログラミングがより効率的かつ効果的になります。

Pythonでクラスを扱う際の注意点

クラスを使う際には、以下の注意点を押さえておくと良いでしょう。

グローバル変数とインスタンス変数の違い

変数には、グローバル変数とインスタンス変数の2種類があります。

グローバル変数はプログラムのどこからでもアクセスできる変数で、通常は関数やクラスの外部で定義されます。インスタンス変数はクラス内で定義され、クラスから生成された各インスタンス固有の変数です。

以下のコードではcontainerはグローバル変数で、abはインスタンス変数です。aはインスタンス変数なので、以下のように直接アクセスすることはできません。

# DataContainerクラスを定義
class DataContainer:
    def __init__(self, a=0, b=0):
        self.a = a  # インスタンス変数 (self.a)
        self.b = b  # インスタンス変数 (self.b)

# DataContainerクラスのインスタンスを生成し、a=5, b=3として初期化し、グローバル変数containerに代入
container = DataContainer(5, 3)

# コンソールにaの値を表示
print(a)  # 出力結果: 5とはならずエラーが発生する

aの値にアクセスしたい場合は以下のようにします。

# DataContainerクラスを定義
class DataContainer:
    def __init__(self, a=0, b=0):
        self.a = a  # 初期値としてaを代入
        self.b = b  # 初期値としてbを代入

# DataContainerクラスのインスタンスを生成し、a=5, b=3として初期化し、グローバル変数containerに代入
container = DataContainer(5, 3)

# コンソールにaの値を表示
print(container.a)  # 出力結果: 5

メソッドの引数にselfを忘れない

Pythonのメソッドは、クラス内で定義される際に常にselfという引数を受け取ります。selfは、そのメソッドがどのインスタンスに対して実行されるかを示します。

このselfを忘れてしまうと、メソッドがインスタンス変数や他のメソッドにアクセスできなくなり、エラーの原因となります。特に初心者が陥りやすいミスなので、メソッドを定義する際には必ずselfを最初に書くよう心がけましょう。

継承を多用しすぎない

継承は非常に強力な機能ですが、過度に使用するとコードが複雑になり、どのクラスがどのメソッドやプロパティを持っているのかを把握しづらくなります。

基本的には明確な理由がある場合にのみ継承を使い、可能であればコンポジション(他のクラスのオブジェクトを含める設計)を検討するのも一つの手です。

先ほどのExtendedDataContainerクラスを定義した例では、下記の様の書き直すことができます。

# DataContainerクラスを定義
class DataContainer:
    # コンストラクタでaとbを初期化
    def __init__(self, a=0, b=0):
        self.a = a  # 初期値としてaを代入
        self.b = b  # 初期値としてbを代入

# Calculatorクラスを定義
class Calculator:
    # コンストラクタでDataContainerインスタンスを受け取る
    def __init__(self, data_container):
        self.data_container = data_container

    # aとbの合計を計算して返すメソッド
    def sum(self):
        return self.data_container.a + self.data_container.b

    # aとbの乗算を計算して返すメソッド
    def multiply(self):
        return self.data_container.a * self.data_container.b

# DataContainerクラスのインスタンスを生成し、a=5, b=3として初期化
data_container = DataContainer(5, 3)

# Calculatorクラスのインスタンスを生成し、data_containerを渡す
calculator = Calculator(data_container)

# sumメソッドを呼び出し、aとbの合計を計算
result_sum = calculator.sum()

# 計算結果をコンソールに表示
print(result_sum)  # 出力結果: 8

# multiplyメソッドを呼び出し、aとbの乗算を計算
result_multiply = calculator.multiply()

# 計算結果をコンソールに表示
print(result_multiply)  # 出力結果: 15

クラス名と変数名の命名規則に従う

Pythonでのクラス名は慣習的にキャメルケースで命名するのが一般的です。例えば、DataContainerBottleNameのように、各単語の頭文字を大文字にして繋げる形式のことです。

一方で、変数名は慣習的にスネークケースで命名するのが一般的です。例えば、result_sumresult_multiplyのように全て小文字で記述し、単語の区切りはアンダーバーを使用する形式のことです。

この命名規則に従うことでコードが読みやすくなり、他のプログラマーと協力して作業する際にも一貫性を保つことができます。また、クラス名や変数名はそのクラスや変数が何を表しているのかが明確にわかるようにするのが望ましいです。

クラスの使いどころ

クラスを使わなくても同じ処理はできる

Pythonではクラスを使用せずとも同じ処理を実装することは可能です。ExtendedDataContainerクラスを定義したコードはdict型の変数(辞書変数)を使用して、下記のように書き直すことができます。

# DataContainerクラスの代わりに辞書変数data_containerを使用してaとbを初期化
data_container = {'a': 5, 'b': 3}

# aとbの合計を計算して返す関数sum_valuesを定義
def sum_values(data):
    return data['a'] + data['b']

# aとbの乗算を計算して返す関数multiply_valuesを定義
def multiply_values(data):
    return data['a'] * data['b']

# sum_values関数を呼び出し、aとbの合計を計算
result_sum = sum_values(data_container)

# 計算結果をコンソールに表示
print(result_sum)  # 出力結果: 8

# multiply_values関数を呼び出し、aとbの乗算を計算
result_multiply = multiply_values(data_container)

# 計算結果をコンソールに表示
print(result_multiply)  # 出力結果: 15

このコードではDataContainer クラスの代わりに辞書(dict)変数 data_container を使用し、Calculator クラスのメソッドはそれぞれの関数sum_valuesmultiply_values として定義しています。

この程度のコードであれば、クラスを使用せずデータの格納と操作を辞書変数と関数でシンプルに実現することができます。

Pythonでクラスを扱うメリット

Pythonでクラスを扱うメリットは下記のとおりです。主に、大規模なプログラムでコードの再利用を向上させ、データと処理を一体化してく管理できることにあります。

  • コードの再利用性が向上する
  • データと処理を一体化して管理できる
  • 大規模なプログラムの構造化が容易になる

この記事ではクラスは「設計図」と例えたので、ここでは「自動車の設計」を例としてクラスを扱うメリットを説明します。下記コードはクラスを使ったコードと使わないコードの記述例です。

クラスを使った設計の記述例

# 自動車の設計のためのCarクラスを定義
class Car:
    # 自動車の各パーツの初期化を行うコンストラクタ
    def __init__(self, engine, transmission, driveshaft, suspension, body, electrical_system, intake_system, exhaust_system, egr_system, drive_system):
        self.engine = engine
        self.transmission = transmission
        self.driveshaft = driveshaft
        self.suspension = suspension
        self.body = body
        self.electrical_system = electrical_system
        self.intake_system = intake_system
        self.exhaust_system = exhaust_system
        self.egr_system = egr_system
        self.drive_system = drive_system

    # 自動車の情報を表示するためのメソッド
    def display_info(self):
        print(f"エンジン: {self.engine}")
        print(f"トランスミッション: {self.transmission}")
        print(f"ドライブシャフト: {self.driveshaft}")
        print(f"サスペンション: {self.suspension}")
        print(f"ボディ: {self.body}")
        print(f"電装: {self.electrical_system}")
        print(f"吸気系: {self.intake_system}")
        print(f"排気系: {self.exhaust_system}")
        print(f"EGRシステム: {self.egr_system}")
        print(f"駆動方式: {self.drive_system}")
        print("-" * 50)

# 軽自動車のインスタンスを作成
kei_car = Car(
    engine={'type': '直列4気筒', 'horsepower': 100},
    transmission={'type': 'CVT', 'gears': 5},
    driveshaft={'material': 'スチール', 'length': 1.5},
    suspension={'type': 'マクファーソンストラット', 'spring_rate': 5.0},
    body={'type': 'ハッチバック', 'color': '赤'},
    electrical_system={'battery_voltage': 12, 'alternator_output': 90},
    intake_system={'air_filter': 'ペーパー', 'intake_manifold': 'アルミ'},
    exhaust_system={'muffler_type': 'スポーツ', 'exhaust_pipe_material': 'ステンレス'},
    egr_system={'egr_valve_type': '電子制御', 'flow_rate': 20},
    drive_system='FF'
)

# セダンのインスタンスを作成
sedan_car = Car(
    engine={'type': 'V6', 'horsepower': 250},
    transmission={'type': 'AT', 'gears': 6},
    driveshaft={'material': 'カーボン', 'length': 2.0},
    suspension={'type': 'ダブルウィッシュボーン', 'spring_rate': 6.0},
    body={'type': 'セダン', 'color': '黒'},
    electrical_system={'battery_voltage': 12, 'alternator_output': 120},
    intake_system={'air_filter': '高性能ペーパー', 'intake_manifold': 'アルミ'},
    exhaust_system={'muffler_type': 'サイレンサー', 'exhaust_pipe_material': 'ステンレス'},
    egr_system={'egr_valve_type': '電子制御', 'flow_rate': 25},
    drive_system='FR'
)

# SUVのインスタンスを作成
suv_car = Car(
    engine={'type': 'V8', 'horsepower': 400},
    transmission={'type': 'AT', 'gears': 8},
    driveshaft={'material': 'スチール', 'length': 2.2},
    suspension={'type': 'マクファーソンストラット', 'spring_rate': 7.0},
    body={'type': 'SUV', 'color': '青'},
    electrical_system={'battery_voltage': 12, 'alternator_output': 150},
    intake_system={'air_filter': 'スポーツ', 'intake_manifold': 'カーボンファイバー'},
    exhaust_system={'muffler_type': 'デュアル', 'exhaust_pipe_material': 'チタン'},
    egr_system={'egr_valve_type': '電子制御', 'flow_rate': 30},
    drive_system='4WD'
)

# スポーツカーのインスタンスを作成
sports_car = Car(
    engine={'type': 'V8', 'horsepower': 500},
    transmission={'type': 'DCT', 'gears': 7},
    driveshaft={'material': 'カーボンファイバー', 'length': 1.8},
    suspension={'type': 'ダブルウィッシュボーン', 'spring_rate': 8.0},
    body={'type': 'クーペ', 'color': '黄色'},
    electrical_system={'battery_voltage': 12, 'alternator_output': 180},
    intake_system={'air_filter': 'スポーツ', 'intake_manifold': 'カーボンファイバー'},
    exhaust_system={'muffler_type': 'レーシング', 'exhaust_pipe_material': 'チタン'},
    egr_system={'egr_valve_type': '電子制御', 'flow_rate': 35},
    drive_system='MR'
)

# 軽自動車の情報を表示
kei_car.display_info()

# セダンの情報を表示
sedan_car.display_info()

# SUVの情報を表示
suv_car.display_info()

# スポーツカーの情報を表示
sports_car.display_info()

クラスを使わない設計の記述例

# 軽自動車のパーツ情報
engine_k = {'type': '直列4気筒', 'horsepower': 100}
transmission_k = {'type': 'CVT', 'gears': 5}
driveshaft_k = {'material': 'スチール', 'length': 1.5}
suspension_k = {'type': 'マクファーソンストラット', 'spring_rate': 5.0}
body_k = {'type': 'ハッチバック', 'color': '赤'}
electrical_system_k = {'battery_voltage': 12, 'alternator_output': 90}
intake_system_k = {'air_filter': 'ペーパー', 'intake_manifold': 'アルミ'}
exhaust_system_k = {'muffler_type': 'スポーツ', 'exhaust_pipe_material': 'ステンレス'}
egr_system_k = {'egr_valve_type': '電子制御', 'flow_rate': 20}
drivetrain_k = {'type': 'FF'}

# セダンのパーツ情報
engine_s = {'type': 'V6', 'horsepower': 250}
transmission_s = {'type': 'AT', 'gears': 6}
driveshaft_s = {'material': 'カーボン', 'length': 2.0}
suspension_s = {'type': 'ダブルウィッシュボーン', 'spring_rate': 6.0}
body_s = {'type': 'セダン', 'color': '黒'}
electrical_system_s = {'battery_voltage': 12, 'alternator_output': 120}
intake_system_s = {'air_filter': '高性能ペーパー', 'intake_manifold': 'アルミ'}
exhaust_system_s = {'muffler_type': 'サイレンサー', 'exhaust_pipe_material': 'ステンレス'}
egr_system_s = {'egr_valve_type': '電子制御', 'flow_rate': 25}
drivetrain_s = {'type': 'FR'}

# SUVのパーツ情報
engine_suv = {'type': 'V8', 'horsepower': 400}
transmission_suv = {'type': 'AT', 'gears': 8}
driveshaft_suv = {'material': 'スチール', 'length': 2.2}
suspension_suv = {'type': 'マクファーソンストラット', 'spring_rate': 7.0}
body_suv = {'type': 'SUV', 'color': '青'}
electrical_system_suv = {'battery_voltage': 12, 'alternator_output': 150}
intake_system_suv = {'air_filter': 'スポーツ', 'intake_manifold': 'カーボンファイバー'}
exhaust_system_suv = {'muffler_type': 'デュアル', 'exhaust_pipe_material': 'チタン'}
egr_system_suv = {'egr_valve_type': '電子制御', 'flow_rate': 30}
drivetrain_suv = {'type': '4WD'}

# スポーツカーのパーツ情報
engine_sp = {'type': 'V8', 'horsepower': 500}
transmission_sp = {'type': 'DCT', 'gears': 7}
driveshaft_sp = {'material': 'カーボンファイバー', 'length': 1.8}
suspension_sp = {'type': 'ダブルウィッシュボーン', 'spring_rate': 8.0}
body_sp = {'type': 'クーペ', 'color': '黄色'}
electrical_system_sp = {'battery_voltage': 12, 'alternator_output': 180}
intake_system_sp = {'air_filter': 'スポーツ', 'intake_manifold': 'カーボンファイバー'}
exhaust_system_sp = {'muffler_type': 'レーシング', 'exhaust_pipe_material': 'チタン'}
egr_system_sp = {'egr_valve_type': '電子制御', 'flow_rate': 35}
drivetrain_sp = {'type': 'MR'}

# 自動車の情報を表示するための関数
def display_car_info(engine, transmission, driveshaft, suspension, body, electrical_system, intake_system, exhaust_system, egr_system, drivetrain):
    print(f"エンジン: {engine}")
    print(f"トランスミッション: {transmission}")
    print(f"ドライブシャフト: {driveshaft}")
    print(f"サスペンション: {suspension}")
    print(f"ボディ: {body}")
    print(f"電装: {electrical_system}")
    print(f"吸気系: {intake_system}")
    print(f"排気系: {exhaust_system}")
    print(f"EGRシステム: {egr_system}")
    print(f"駆動方式: {drivetrain}")
    print("-" * 50)

# 軽自動車の情報を表示
display_car_info(engine_k, transmission_k, driveshaft_k, suspension_k, body_k, electrical_system_k, intake_system_k, exhaust_system_k, egr_system_k, drivetrain_k)

# セダンの情報を表示
display_car_info(engine_s, transmission_s, driveshaft_s, suspension_s, body_s, electrical_system_s, intake_system_s, exhaust_system_s, egr_system_s, drivetrain_s)

# SUVの情報を表示
display_car_info(engine_suv, transmission_suv, driveshaft_suv, suspension_suv, body_suv, electrical_system_suv, intake_system_suv, exhaust_system_suv, egr_system_suv, drivetrain_suv)

# スポーツカーの情報を表示
display_car_info(engine_sp, transmission_sp, driveshaft_sp, suspension_sp, body_sp, electrical_system_sp, intake_system_sp, exhaust_system_sp, egr_system_sp, drivetrain_sp)

クラスを使用することで、パーツの変更がクラス内で一元管理されるため車種の追加が容易です。クラスを使用しない場合、車種ごとにパーツ情報のための変数を追加しなければなりません。設計が複雑になればなるほど、変数の管理が難しくなります。

クラスの使用グローバル変数インスタンス変数変数総数
クラスを使用する41014
クラスを使用しない40040
変数の使用総数

クラスを使用する場合Carクラスを使って共通の設計ロジックを再利用できます。新しい車種を追加する場合でも、クラスを利用することでコードの重複を避け、管理しやすくなります。

この例の場合、更に新しい車種を追加するならば、クラスを使用していればグローバル変数を1種類追加するだけですが、クラスを使用していない場合はグローバル変数を10種類追加しなければなりません。

まとめ

Pythonのクラスは、オブジェクト指向プログラミングの基礎を理解し、効率的にコードを管理するための重要なツールです。

初心者にとっては少し難しい概念かもしれませんが、この記事で紹介した基本的な使い方や注意点を押さえておけば、クラスを効果的に活用できるようになるでしょう。Pythonを使いこなすために、ぜひクラスの概念をマスターしてください。

よかったらシェアしてね!
  • URLをコピーしました!
目次