Q1次のうち、Python のクラス定義として正しいものはどれですか?
クラスとインスタンス — 自分だけの型を定義する
PythonのクラスとインスタンスをProductクラスを題材に解説。classで自分だけの型を定義し、__init__でname/priceを初期化、self.show()のメソッド呼び出しまで手を動かして確認できます。
前回の型まとめで触れたとおり、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)