/
Time Limit: 0 msec / Memory Limit: 0 KiB
キーポイント
- class により変数と処理をまとめた新しい型を定義することができる
__init__でコンストラクタを定義する変数名.インスタンス変数名でインスタンス変数にアクセスすることができる- インスタンスメソッド内でインスタンス変数を参照するには
self.インスタンス変数名とする。
class
class の基礎
変数と処理をまとめた型を class といいます。例えば set や list などもクラスです。
クラスは自分で作ることができます。まずは簡単な例から考えてみましょう。人を表すクラス 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 によりインスタンス変数 age に 18 が代入されています。
インスタンス変数には 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。
問題
本節には練習問題はありません。
-
インスタンスのことをオブジェクトと呼ぶこともありますが、オブジェクトという言葉は別のものを指すこともあるため、インスタンスと呼ぶ方がよいでしょう。 ↩
-
__init__は厳密にはイニシャライザと呼ばれ、コンストラクタは別のものを指しますが、一般的な文脈においては__init__をコンストラクタと呼ぶことが広く慣行となっています。 ↩ -
普通の関数と同様に、メソッドの引数には自由に名前をつけることができますが、1つ目の引数の名前は
selfにすることが強く推奨されており、競技プログラミング用途であるかによらずほとんどのPythonのコードがそのように書かれています。 ↩ -
ここで扱うには高度な内容であるため、セグメントツリーやDSUがどのようなデータ構造であるかの説明は省略します。 ↩