AK - 4.08.class Editorial /

Time Limit: 0 msec / Memory Limit: 0 KiB

前のページ | 次のページ

キーポイント

  • class により変数と処理をまとめた新しい型を定義することができる
  • __init__ でコンストラクタを定義する
  • 変数名.インスタンス変数名 でインスタンス変数にアクセスすることができる
  • インスタンスメソッド内でインスタンス変数を参照するには self.インスタンス変数名 とする。

class

class の基礎

変数と処理をまとめた型を class といいます。例えば setlist などもクラスです。
クラスは自分で作ることができます。まずは簡単な例から考えてみましょう。人を表すクラス Person を以下のように作り、名前と年齢を持つようにします。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person("Taro", 18)  # Taroを作成
print(p.name)  # Taro  名前を出力
q = Person("Jiro", 17)  # Jiroを作成
print(q.age)  # 17  年齢を出力

Person クラスは人を定義するものであり、Person インスタンス(上の例では変数 p, q が持つもの)は個々の人を指します1。例えば「車」という言葉は車という概念と個々の具体的な車を両方指しますが、python でのプログラミングではこの2つを「クラス」「インスタンス」と呼び分けるイメージです。
Person インスタンスが持つ変数 name, ageインスタンス変数と呼びます。

__init__コンストラクタと呼ばれる特別なメソッドです2。引数の1番目に自分自身、2番目以降にインスタンス生成時に渡される引数を受け取ります。
Person クラスの例では、6 行目の Person("Taro", 18) により __init__(self, "Taro", 18) とコンストラクタが呼ばれ、self.name = name によりインスタンス変数 name"Taro" が、 self.age = age によりインスタンス変数 age18 が代入されています。

インスタンス変数には p.name のように 変数名.インスタンス変数名 でアクセスすることができます。

メソッド

インスタンスに紐づいている関数のようなものをメソッドといいます。例えば list.sort() はメソッドです。
class にはメソッドを定義することができます。以下の例では、Person クラスに各自の名前を出力するメソッド say_hello() を実装しています。メソッドの中で自身の持つインスタンス変数を参照する際は self.インスタンス変数名 とします。3

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def say_hello(self):
        print(f"Hello, I'm {self.name}.")

p = Person("Taro", 18)
p.say_hello()  # "Hello, I'm Taro."
q = Person("Jiro", 17)
q.say_hello()  # "Hello, I'm Jiro."

メソッドはコンストラクタ同様、引数の1番目に自分自身を表す変数を取り、2番目以降に通常の引数を取ります。上の例で、p.say_hello() で引数を渡していませんが、def say_hello(self): では引数が1つあるのはこのためです。別の例を以下の挙げます。

class Calculator:
    def __init__(self, x):
        self.x = x
    def get_x(self):
        return self.x
    def add(self, y):
        self.x += y

c = Calculator(10)
print(c.get_x())  # 10
c.add(31)
print(c.get_x())  # 41

メソッド内ではインスタンス変数を新たに定義することもできます。ただし、一般に __init__ で定義されていないインスタンス変数を新たに定義することは可読性が低くなるため控えた方がよいです。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def add_money(self):
        self.money = 100
    def show_money(self):
        print(self.money)

p = Person("Taro", 18)
p.add_money()
p.show_money()  # 100
q = Person("Jiro", 17)
# q.show_money()  # インスタンス変数 money が定義されていないためエラー

特殊メソッド

__init__ のように、前後を2個のアンダースコアで挟まれたメソッドを特殊メソッドといいます。これらを定義することで、演算子による演算の内容を定義したり、print で表示されるものを定義することができます。

以下の例は __add__ メソッドにより + 演算子の内容を定義し、__str__ メソッドにより print で表示されるものを定義しています。その他の特殊メソッドについては、公式ドキュメントなどを参照してください。

特殊メソッド名

class Vector2d:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __add__(self, other):
        return Vector2d(self.x + other.x, self.y + other.y)
    def __str__(self):
        return f"(x={self.x}, y={self.y})"

v = Vector2d(3, 4)
w = Vector2d(10, 20)
print(v+w)  #  (x=13, y=24)

class を利用するメリット

以下の例は、リストとその要素の総和を同時に管理するクラスの例です。

class List_with_sum:
    def __init__(self, array):
        self.array = array
        self.sum = sum(array)
    def append(self, x):
        self.array.append(x)
        self.sum += x
    def pop(self, i=-1):
        x = self.array.pop(i)
        self.sum -= x
        return x
    def get_sum(self):
        return self.sum
    def __str__(self):
        return str(self.array)

a = List_with_sum([20, 30, 10])
print(a.get_sum())  # 出力: 60
print(a.pop())  # 出力: 10
print(a.get_sum())  # 出力: 50
a.append(60)
print(a.get_sum())  # 出力: 110
print(a)  # 出力: [20, 30, 60]

このクラスでは、要素を追加・削除するときに総和も自動で更新されます。このため、このクラスを利用する際に総和が常に正しい値に保たれることを意識せずに済みます。
一方、クラスを使わずに同じことを行おうとすると、要素を追加・削除するたびに総和を手動で更新する必要があり、更新忘れによるバグが発生しやすくなります。
このように、操作とそれによる状態の更新をまとめて行いたい場面ではクラスを作成することにメリットがあります。
また、データ構造を表すクラスを適切に用意することで、同じクラスを他の問題にも使い回せることがあります。競技プログラミングにおいては、セグメントツリーや DSU などが典型的な例です4

問題

本節には練習問題はありません。

前のページ | 次のページ


  1. インスタンスのことをオブジェクトと呼ぶこともありますが、オブジェクトという言葉は別のものを指すこともあるため、インスタンスと呼ぶ方がよいでしょう。 

  2. __init__ は厳密にはイニシャライザと呼ばれ、コンストラクタは別のものを指しますが、一般的な文脈においては __init__ をコンストラクタと呼ぶことが広く慣行となっています。 

  3. 普通の関数と同様に、メソッドの引数には自由に名前をつけることができますが、1つ目の引数の名前は self にすることが強く推奨されており、競技プログラミング用途であるかによらずほとんどのPythonのコードがそのように書かれています。 

  4. ここで扱うには高度な内容であるため、セグメントツリーやDSUがどのようなデータ構造であるかの説明は省略します。