↓ メインコンテンツへスキップ
  1. 第06講 クラス(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.nameと self.age はそれぞれPersonオブジェクト(p)に紐づいた変数です。 name と age はコンストラクタの引数で受け取った値を表しています。 以下の図で示すように、self.name と name は異なる変数ですが、指し示す値は同じものになります。 コンストラクタを抜けると、name は参照できなくなりますが、Person オブジェクトの name(コンストラクタ内では self.name)経由で参照できるようになっています。 また、name、self.name、p.name の関係性は以下の図を参照してください。

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

コンストラクタに、オブジェクトに必要な値を渡して、オブジェクトを初期化するのが一般的です。 記念日や三角形のように、作った後で値を変える必要がないオブジェクトは、コンストラクタで値を決めたらそのまま使います。 一方、今回の課題のスタックのように、後から値が変わるオブジェクトもあります。 その場合も、外から直接メンバ変数を書き換えるのではなく、push のような操作を表すメソッドを用意して、メソッドを通して更新するのが基本です。

例題1. 記念日クラス
#

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

class Anniversary:
    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 = Anniversary(12, 25, "クリスマス")
assert h1.str() == "12月25日はクリスマスです。", "クリスマスの記念日が正しく出力されていません。"
h1.print() # 12月25日はクリスマスです。

new_years_day = Anniversary(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のオブジェクトを生成できます。 つまり、引数なしのコンストラクタが暗黙的に定義されていると考えることができます。 そのため、オブジェクトを作成するときに、特に引数を渡す必要がなければ、コンストラクタを定義する必要はありません。