Q1次のうち、Python のクラス定義 として正しいものはどれですか?
クラスとインスタンス — 自分だけの型を定義する
Python のクラスとインスタンスを基礎から解説します。class の定義、self の役割、__init__ で属性を初期化する基本までを図解で押さえます。
前回の型まとめで触れたとおり、int や str、list、dict といった組み込み型だけでは「ユーザー」「商品」「注文」のような業務上の概念をそのまま表せません。今回はその次のステップとして、class で自分だけの型を定義する書き方 — オブジェクト指向プログラミング(OOP)を整理します。
なぜオブジェクト指向か
ここまで学んだ書き方は 手続き型 と呼ばれ、関数を組み合わせて処理を組み立てるスタイルでした。プログラムが大きくなると、関連するデータと処理がコードのあちこちに散らばるようになり、変更や再利用が難しくなります。
オブジェクト指向プログラミング(OOP) では、関連するデータ(属性)とそれに対する操作(メソッド)を 1 つのまとまり — オブジェクト — としてくくります。これにより、現実世界の概念のかたまりに合わせてコードを設計できるようになります。
関数(データ) のように毎回データを渡して使う。OOP は両者を オブジェクトの中 に同居させ、一体として扱う。クラスは設計図、インスタンスは実体
OOP の中心となる 2 つの言葉を押さえましょう。
- クラス(class) — 「どんな属性とメソッドを持つか」を書いた 設計図
- インスタンス(instance) — クラスをもとに作られる 実体
たとえば「商品」という概念を Product クラスとして定義すれば、そこから「リンゴ」「バナナ」「みかん」といった個別の商品インスタンスをいくつでも作れます。クラスとインスタンスをまとめて オブジェクト と呼びます。
最小のクラスを定義してみる
では実際に、商品を表す Product クラスを定義してみましょう。クラスは class クラス名: という構文で書きます。Python ではクラス名は 大文字始まりのキャメルケース(Product、UserAccount など)にするのが慣習です。
クラスの中に直接 name = "りんご" のように書いた変数は、クラスが持つ既定値として扱われ、Product.name のように クラス名から直接アクセス できます。
class Product:
name = "りんご"
price = 150
# クラスから直接アクセスできる
print(Product.name) # りんご
print(Product.price) # 150
class のインデント直下に書いた name / price は、クラスそのものに張り付く変数で、Product.name のようにクラス名から直接読める。このように class 直下に書いた変数はすべて クラス変数 と呼ばれる種類です。インスタンスごとに別々の値を持たせる インスタンス変数 とは性質が違いますが、その違いは後の記事で改めて整理します。今は 「クラス自身に値を張り付けて、Product.name で読める」 という最小形だけ理解できれば十分です。
__init__ でインスタンスごとに違う値を持たせる
上の最小クラスは name = "りんご" のような 固定値 をクラスに張り付けていただけなので、これだとどんなインスタンスを作っても全部「りんご」になってしまいます。実際の業務では、1 つのクラスから「りんご」「バナナ」「みかん」のように違う値を持つインスタンスを いくつでも作りたいはずです。
そのために使うのが、Python の特別なメソッド __init__ — コンストラクタ とも呼ばれる初期化メソッドです。Product("りんご", 150) のように引数付きで呼び出すと、Python が自動的に __init__ を呼び出し、その中で self.name = name のように そのインスタンス専用の属性 に値を書き込めます。
アンダースコア 2 つ
__init__ のように アンダースコア 2 つで囲まれた名前 は、Python が特別な意味を持たせている ダンダーメソッド と呼ばれます。インスタンス生成時の __init__、print() 表示時の __str__ など多数あり、それぞれ 自動的に呼び出されるタイミング が決まっています。__init__ の詳しい使い方や、対になる __del__ は 次回の記事 で改めて扱います。
class Product:
def __init__(self, name, price): # インスタンス生成時に自動で呼ばれる
self.name = name
self.price = price
apple = Product("りんご", 150)
banana = Product("バナナ", 80)
print(apple.name, apple.price) # りんご 150
print(banana.name, banana.price) # バナナ 80
# apple と banana は別のオブジェクト
print(apple is banana) # False
Product("りんご", 150) を呼ぶと、Python は ① 空のインスタンスを用意し、② それを self として __init__ に渡し、③ 引数で受け取った値を そのインスタンスの属性 として書き込んで完成させる。Product(...) を呼ぶたびに 別の実体 が生まれる。apple も banana も orange も 同じ設計図から作られた別のオブジェクト で、それぞれが自分の name / price を持つ。メソッドと self の仕組み
属性だけだと「データを置いておけるだけ」で、まだ普通の辞書とそう変わりません。インスタンスに対してできる操作 を一緒に書いておけるのが OOP の本領で、それを メソッド と呼びます。
def で書いた関数が メソッド。第 1 引数 self は必須 で、呼び出し時には 「呼び出し元のインスタンス」 が自動で入る。メソッド内では self.name のように書くことで、そのインスタンスの属性にアクセスできる。apple.show() と書くと、Python は内部的に Product.show(apple) を実行していて、self の中身が apple になります。
- apple, banana は別々のインスタンス
apple.show()を呼ぶと…
- self に apple が自動的に入る
self.nameは apple.name を読むbanana.show()のときはselfがbananaになる
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def show(self): # 第 1 引数は必ず self
print(f"{self.name}: {self.price} 円")
apple = Product("りんご", 150)
banana = Product("バナナ", 80)
apple.show() # りんご: 150 円 Product.show(apple) と内部的には同じ
banana.show() # バナナ: 80 円 Product.show(banana) と内部的には同じ
apple.show() を呼ぶと self に apple が入り、banana.show() のときは self に banana が入る。同じ show メソッドでも、呼び出したインスタンスごとに別の self で動く。self は単なる「慣習」
Python 文法上は第 1 引数の名前は何でもよく、def show(this): でも動きます。ただしほぼすべての Python コードが self を使っているため、揃えるのが鉄則です。self 以外を使うと読み手の混乱を招きます。
今回の記事では、オブジェクト指向の基本を学びました。変数やメソッドにはのちの記事でより詳細に解説します。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2メソッドの第 1 引数 self には、呼び出し時に何が自動的に渡されますか?
Q3次のコードを実行したときの出力はどれですか?class P: def __init__(self, x): self.x = xa = P(10)b = P(20)print(a.x + b.x)