メインコンテンツへスキップ
  1. 第08講 クラス(1/2)/

コンストラクタ

クラス メソッド コンストラクタ 初期化
目次

コンストラクタ
#

教科書 p.221(Lesson 8.2)

オブジェクトを生成する際に,そのオブジェクトの初期化を行うメソッドを コンストラクタ(Constructor) と呼びます. コンストラクタは,クラスの中で __init__ という名前のメソッドとして定義します.

例えば,Person オブジェクト作成時に必ず名前と年齢を指定したい場合は, 以下のようにコンストラクタに名前と年齢を受け取るよう引数を定義します.

class Person:
    def __init__(self, name, age): # コンストラクタ,名前と年齢を受け取る.
        self.name = name
        self.age = age

# p = Person() # 引数の数が合わないため,エラーが発生する.
p = Person("John Doe", 30) # Person オブジェクト p を生成するときに名前と年齢を指定する.
print(f"{p.name} is {p.age} years old.") # コンストラクタで指定したので,名前と年齢が出力される.

self.nameself.age はそれぞれPersonオブジェクト(p)に紐づいた変数です. nameage はコンストラクタの引数で受け取った値を表しています. 以下の図で示すように,self.namename は異なる変数ですが,指し示す値は同じものになります. コンストラクタを抜けると,name は参照できなくなりますが,Person オブジェクトの name(コンストラクタ内では self.name)経由で参照できるようになっています. また,nameself.namep.name の関係性は以下の図を参照してください.

コンストラクタとインスタンス変数

コンストラクタに,オブジェクトに必要な値を渡して,オブジェクトを初期化するのが一般的です. 近年では,イミュータブル(変更不可)であることが求められることが多いため,コンストラクタで値を初期化し,値を更新しないことが多いです. もし,値の更新が必要な場合は,古いオブジェクトの値を更新せず,新しいオブジェクトを作成するのが一般的です.

例題1. 記念日クラス
#

次のように,記念日を表す Aniversary クラスを定義します. コンストラクタで,月,日,名前を受け取り,インスタンス変数にそれぞれ代入します. printメソッドで,記念日を出力します.

class Aniversary:
    def __init__(self, month, day, name):
        self.month = month
        self.day = day
        self.name = name

    def print(self):
        print(self.str())

    def str(self):
        return f"{self.month}{self.day}日は{self.name}です."

h1 = Aniversary(12, 25, "クリスマス")
assert h1.str() == "12月25日はクリスマスです.", "クリスマスの記念日が正しく出力されていません."
h1.print() # 12月25日はクリスマスです.

new_years_day = Aniversary(1, 1, "元日")
assert new_years_day.str() == "1月1日は元日です.", "元日の記念日が正しく出力されていません."
new_years_day.print() # 1月1日は元日です.

例題2. 三角形クラス
#

次のように,三角形を表す Triangle クラスを定義します. コンストラクタで,3つの辺の長さを受け取り,インスタンス変数にそれぞれ代入します.

class Triangle:
    def __init__(self, line1, line2, line3):
        self.line1 = line1
        self.line2 = line2
        self.line3 = line3
    
    def is_valid(self): # 有効な三角形かどうかを判定する.
        return self.line1 > 0 and self.line2 > 0 and self.line3 > 0 and \
                self.line1 + self.line2 > self.line3 and \
                self.line2 + self.line3 > self.line1 and \
                self.line3 + self.line1 > self.line2

    def area(self):
        if not self.is_valid():
            return 0
        return self.calculate_area_helons_formula()

    def calculate_area_helons_formula(self): # ヘロンの公式から面積を求める.
        s = (self.line1 + self.line2 + self.line3) / 2
        return (s * (s - self.line1) * (s - self.line2) * (s - self.line3)) ** 0.5

t1 = Triangle(3, 4, 5)
assert t1.is_valid(), "有効な三角形であるはずです."
assert t1.area() == 6.0, "面積は 6.0 であるはずです."

t2 = Triangle(1, 1, 3)
assert not t2.is_valid(), "有効な三角形ではないはずです."
assert t2.area() == 0, "面積は 0 であるはずです."

この Triangle クラスのようにフィールド(変数,ここでは line1, line2, line3)とそれに関連するメソッド(関数.ここでは is_valid, area, calculate_area_helons_formula)をまとめたものを クラス と呼びます.

コンストラクタがないクラス
#

クラスの最低限の定義は,以下のようになります.

class TinyClass:
    pass

このクラスはコンストラクタが定義されていませんが,TinyClass() という呼び出しで,TinyClassのオブジェクトを生成できます. つまり,引数なしのコンストラクタが暗黙的に定義されていると考えることができます. そのため,オブジェクトを作成するときに,特に引数を渡す必要がなければ,コンストラクタを定義する必要はありません.