順番に読み進めながら学べます

クラスとインスタンス — 自分だけの型を定義する

PythonのクラスとインスタンスをProductクラスを題材に解説。classで自分だけの型を定義し、__init__でname/priceを初期化、self.show()のメソッド呼び出しまで手を動かして確認できます。

前回の型まとめで触れたとおり、intstrlistdictといった組み込み型だけでは「ユーザー」「商品」「注文」のような業務上の概念をそのまま表せません。今回はその次のステップとして、class で自分だけの型を定義する書き方 — オブジェクト指向プログラミング(OOP)を整理します。

なぜオブジェクト指向か

ここまで学んだ書き方は手続き型と呼ばれ、関数を組み合わせて処理を組み立てるスタイルでした。プログラムが大きくなると、関連するデータと処理がコードのあちこちに散らばるようになり、変更や再利用が難しくなります。

オブジェクト指向プログラミング(OOP)では、関連するデータ(属性)とそれに対する操作(メソッド)を 1 つのまとまり — オブジェクト — としてくくります。これにより、現実世界の概念のかたまりに合わせてコードを設計できるようになります。

手続き型 vs オブジェクト指向 — データと関数の関係
手続き型データ(変数)関数(別の場所)OOPオブジェクトデータ + メソッド1 つに一体化別物 (毎回渡す)結果
手続き型ではデータと関数が完全に別の場所にあり、関数(データ)のように毎回データを渡して使う。OOP は両者をオブジェクトの中に同居させ、一体として扱う。

クラスは設計図、インスタンスは実体

OOP の中心となる 2 つの言葉を押さえましょう。

- クラス(class) — 「どんな属性とメソッドを持つか」を書いた設計図

- インスタンス(instance) — クラスをもとに作られる実体

たとえば「商品」という概念をProductクラスとして定義すれば、そこから「リンゴ」「バナナ」「みかん」といった個別の商品インスタンスをいくつでも作れます。クラスとインスタンスをまとめてオブジェクトと呼びます。

クラスから複数のインスタンスを作る
Product(クラス)applename='りんご'banananame='バナナ'orangename='みかん'生成生成生成
クラスは型紙のようなもので、それ自体は使う対象にならない。クラスから生まれる実体(インスタンス)が実際にデータを持ち、メソッドを呼ばれる。

最小のクラスを定義してみる

では実際に、商品を表すProductクラスを定義してみましょう。クラスはclass クラス名:という構文で書きます。Python ではクラス名は大文字始まりのキャメルケースProductUserAccountなど)にするのが慣習です。

クラスの中に直接name = "りんご"のように書いた変数は、クラスが持つ既定値として扱われ、Product.nameのようにクラス名から直接アクセスできます。

class Product:
    name = "りんご"
    price = 150

# クラスから直接アクセスできる
print(Product.name)    # りんご
print(Product.price)   # 150
クラス本体に張り付く変数 — クラス変数
Product(クラス本体)name='りんご'price=150持つ持つ
classのインデント直下に書いたname / priceは、クラスそのものに張り付く変数で、Product.nameのようにクラス名から直接読める。

このようにclass直下に書いた変数はすべてクラス変数と呼ばれる種類です。インスタンスごとに別々の値を持たせるインスタンス変数とは性質が違いますが、その違いは後の記事で改めて整理します。今は「クラス自身に値を張り付けて、Product.name で読める」という最小形だけ理解できれば十分です。

在庫管理を題材にクラスを 1 つ作ってみます。

class Product:を定義してください。

② クラスの中にname = "りんご"price = 150stock = 10の 3 つを書いてください。

print(Product.name) / print(Product.price) / print(Product.stock)の 3 行を実行し、クラス名から直接値を取り出せることを確認してください。

(正しく実行できれば解説が表示されます)

Python エディタ

コードを実行してください

__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) を呼ぶと何が起きるか
apple = Product( 'りんご', 150)__init__(self, name, price)self.name = nameself.price = priceapplename='りんご', price=150自動呼び出し代入完成
Product("りんご", 150)を呼ぶと、Python は ① 空のインスタンスを用意し、② それをselfとして__init__に渡し、③ 引数で受け取った値をそのインスタンスの属性として書き込んで完成させる。
1 つのクラスから複数のインスタンスを作る
Product(クラス)applename='りんご', price=150banananame='バナナ', price=80orangename='みかん', price=120
Product(...)を呼ぶたびに別の実体が生まれる。applebananaorange同じ設計図から作られた別のオブジェクトで、それぞれが自分のname / priceを持つ。

__init__を持つProductクラスを書いて、複数のインスタンスを作ります。

class Product:を定義し、def __init__(self, name, price):の中でself.name = nameself.price = priceを書いてください。

apple = Product("りんご", 150)banana = Product("バナナ", 80)の 2 つのインスタンスを作成してください。

print(apple.name, apple.price)print(banana.name, banana.price)を実行し、それぞれが別の値を持っていることを確認してください。

(正しく実行できれば解説が表示されます)

Python エディタ

コードを実行してください

メソッドと self の仕組み

属性だけだと「データを置いておけるだけ」で、まだ普通の辞書とそう変わりません。インスタンスに対してできる操作を一緒に書いておけるのが OOP の本領で、それをメソッドと呼びます。

メソッドと self の決まりごと
class Product:def show(self): ...第 1 引数self (必須)呼び出し元のインスタンスが自動で入るself.name→ 属性参照中に定義宣言中身経由
クラスの中にdefで書いた関数がメソッド第 1 引数 self は必須で、呼び出し時には「呼び出し元のインスタンス」が自動で入る。メソッド内ではself.nameのように書くことで、そのインスタンスの属性にアクセスできる。

apple.show()と書くと、Python は内部的にProduct.show(apple)を実行していて、selfの中身がappleになります。

self は「呼び出した本人」を指す
モジュール
  • apple, bananaは別々のインスタンス
  • apple.show()を呼ぶと…
show(self) のフレーム
  • selfappleが自動的に入る
  • self.nameapple.nameを読む
  • banana.show()のときはselfbananaになる
self は固定された値ではなく、メソッドを呼んだそのインスタンスが毎回入る。同じ show メソッドでも、apple から呼べば apple の name が、banana から呼べば banana の name が読まれる。
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) と内部的には同じ
メソッド呼び出し時に self が決まる
apple.show()self = appleりんご: 150 円banana.show()self = bananaバナナ: 80 円渡す出力渡す出力
apple.show()を呼ぶとself に apple が入りbanana.show()のときはself に banana が入る。同じshowメソッドでも、呼び出したインスタンスごとに別の self で動く。

Productにメソッドを 1 つ追加して、インスタンス経由で呼び出します。

class Product:を定義し、__init__(self, name, price)self.name / self.priceを初期化してください。

② クラス内にdef show(self):を定義し、print(f"{self.name}: {self.price} 円")を表示してください。

apple = Product("りんご", 150)banana = Product("バナナ", 80)を作り、apple.show()banana.show()をそれぞれ呼び出して、同じメソッドが各インスタンスの値で動くことを確認してください。

Python エディタ

コードを実行してください

self は単なる「慣習」

Python 文法上は第 1 引数の名前は何でもよく、def show(this):でも動きます。ただしほぼすべての Python コードが self を使っているため、揃えるのが鉄則です。self以外を使うと読み手の混乱を招きます。

今回の記事では、オブジェクト指向の基本を学びました。変数やメソッドにはのちの記事でより詳細に解説します。

QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1次のうち、Python のクラス定義として正しいものはどれですか?

Q2メソッドの第 1 引数selfには、呼び出し時に何が自動的に渡されますか?

Q3次のコードを実行したときの出力はどれですか?
class P:
def __init__(self, x):
self.x = x
a = P(10)
b = P(20)
print(a.x + b.x)