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

クラス変数とインスタンス変数 — どこに値が存在するか

Python のクラス変数とインスタンス変数の違いを図解で解説します。共有される値と各インスタンス固有の値、self.x = ... で何が起きるかまで押さえます。

前回はコンストラクタ __init__ を必須化やデフォルト引数で実用的に使う書き方、対になる __del__ までを整理しました。今回はその上で、クラスに登場する 2 種類の変数 — クラス変数とインスタンス変数 — の違いと、これまで何度も書いてきた self.x = ...メモリ上で何をしているのか を整理します。

2 種類の変数がある

Python のクラスに書く変数には、大きく分けて次の 2 種類 があります。

- クラス変数 — クラス直下(class Product: のすぐ下)に書く。全インスタンスで共有される値

- インスタンス変数 — メソッドの中で self.x = ... と書く。各インスタンスが個別に持つ

どちらも apple.name のような同じドット記法でアクセスでき、見た目では違いが分かりません。だからこそ「どこに値が存在するか」をきちんと意識する必要があります。

class Product:
    tax_rate = 0.1                 # クラス変数(全員で共有)

    def __init__(self, name):
        self.name = name           # インスタンス変数(各自が持つ)

apple  = Product("りんご")
banana = Product("バナナ")
値が存在する場所が違う
Productクラス本体tax_rate=0.1(クラス変数)applename='りんご'banananame='バナナ'持つ共有参照共有参照
クラス変数 tax_rateProduct 1 か所に置かれ、applebanana同じ場所を見に行く。インスタンス変数 name は各インスタンスが自分のメモリの中に持つ

クラス変数 — 全員で 1 つを共有

クラス変数は class Product: のインデント直下tax_rate = 0.1 のように書きます。メソッドの外に書くのがポイントです。クラス自身が 1 つだけ持ち、そのクラスから作られた全インスタンスで共有されます。

アクセスは Product.tax_rate(クラス経由)でも apple.tax_rate(インスタンス経由)でもできます。後者の場合、Python は まずインスタンスを探し、見つからなければクラスを探す という順で値を引きに行きます。

変数の探索範囲 — インスタンス → クラスの順
Product(クラスの変数領域)
  • tax_rate = 0.1 ← クラス変数(共有)
apple(インスタンスの変数領域)
  • name = 'りんご' ← インスタンス変数
banana(インスタンスの変数領域)
  • name = 'バナナ' ← インスタンス変数
Python は属性を探すとき まずインスタンスの中 を見て、無ければ 外側のクラス に問い合わせる。インスタンスから見れば、外側のクラス変数は 共通の傘 のように被さっている。
class Product:
    tax_rate = 0.1                 # クラス変数(全員で共有)

    def __init__(self, name, price):
        self.name = name           # インスタンス変数(各自が持つ)
        self.price = price

apple = Product("りんご", 150)
banana = Product("バナナ", 80)

print(Product.tax_rate)   # 0.1(クラス経由)
print(apple.tax_rate)     # 0.1(インスタンス経由でも読める)
print(banana.tax_rate)    # 0.1

# 同じメモリを指している
print(id(apple.tax_rate) == id(Product.tax_rate))  # True

クラス変数を 1 つ追加して、クラスからもインスタンスからも読めることを確認します。

class Product: の中に クラス変数 currency = "JPY" を 1 行で書いてください(__init__ の上)。

__init__(self, name, price)self.name = name / self.price = price を書いてください。

apple = Product("りんご", 150) を作り、print(Product.currency)print(apple.currency) の両方を実行して、同じ値が表示されることを確認してください。

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

Python エディタ

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

インスタンス変数 — 各自が持つ

インスタンス変数は メソッドの中で self.x = ... と書いた時点で、そのインスタンス専用の属性として作成 されます。多くは __init__ の中で self.name = name のように初期化しますが、__init__ 以外のメソッドの中で self.x = ... と書いても、その瞬間に 新しいインスタンス変数 が作られます。

インスタンス変数は 各インスタンスが個別のメモリに持つため、apple.name を変えても banana.name には一切影響しません。

インスタンス変数は各インスタンスが個別に持つ
apple(インスタンス)name = 'りんご'(独立メモリ)banana(インスタンス)name = 'バナナ'(独立メモリ)持つ持つ
apple.namebanana.name別のメモリ位置 にある。apple.name を書き換えても banana.name には一切影響しない。
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

# id() を見ると別の場所を指している
print(id(apple.name) == id(banana.name))   # False

インスタンス変数が 個別 であることを id() で体感します。

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

apple = Product("りんご", 150)banana = Product("バナナ", 80) を作ってください。

print(apple is banana)print(id(apple.name) == id(banana.name)) の 2 行を実行し、両方とも False が返ることを確認してください。

Python エディタ

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

self.x = ... でインスタンス変数が作られる

クラス変数 tax_rate = 0.1 を持つ Product で、あるインスタンス apple に対して apple.tax_rate = 0.05 と書くとどうなるでしょうか?

結論から言うと、クラス変数は書き換えられずapple の中に 新しいインスタンス変数 tax_rate が作られます。以後 apple.tax_rate を読むと、Python は まずインスタンスを探し、見つかったのでクラスは見に行かないため、新しい 0.05 が返ります。banana には何の影響もありません。

class Product:
    tax_rate = 0.1

    def __init__(self, name, price):
        self.name = name
        self.price = price

apple = Product("りんご", 150)
banana = Product("バナナ", 80)

# apple だけに新しい税率を持たせる
apple.tax_rate = 0.05

print(apple.tax_rate)      # 0.05(apple のインスタンス変数)
print(banana.tax_rate)     # 0.1 (クラス変数を見に行く)
print(Product.tax_rate)    # 0.1 (クラス変数は変わっていない)
代入前と代入後で何が変わるか
apple(インスタンス変数なし)banana(インスタンス変数なし)Producttax_rate=0.1apple.tax_rate=0.05★ 新規tax_rate=0.05appleのインスタンス変数banana(インスタンス変数なし)Producttax_rate=0.1(変わらず)共有参照参照参照
上段(代入前): applebanana もクラスの tax_rate=0.1 を共有参照。下段(apple.tax_rate = 0.05 実行後): apple の中に 新しいインスタンス変数 が誕生し、apple だけそちらを読むようになる。banana とクラスは変わらず。

クラス変数を「インスタンス経由で書き換えられる」と誤解しない

apple.tax_rate = 0.05 を見ると一見「クラス変数を更新した」ように見えますが、実際には apple の中に新しいインスタンス変数を作っただけ です。共有しているクラス変数そのものを変えたいときは、Product.tax_rate = 0.05 のように クラス経由で代入 する必要があります。

self.x = ... でインスタンス変数が新しく作られることを実際に確認します。

class Product: を定義し、クラス変数 tax_rate = 0.1__init__(self, name)self.name = name を書いてください。

apple = Product("りんご")banana = Product("バナナ") を作ってください。

apple.tax_rate = 0.05 を実行したあと、print(apple.tax_rate) / print(banana.tax_rate) / print(Product.tax_rate) の 3 行を実行し、apple だけ値が変わって他は 0.1 のまま であることを確認してください。

Python エディタ

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

クラス変数を更新したいとき

「全インスタンスの税率を一斉に変えたい」「これまで作った商品の累計数を数えたい」のように、クラスがまとめて持っておきたい状態 はクラス変数経由で更新します。

下の例の Product.created_count「これまでに作られた商品の数」 をクラス自身に持たせています。__init__ の中で Product.created_count += 1 と書くと、どのインスタンスから作っても同じ場所を更新するため、全体の合計が正しく数えられます。これを self.created_count += 1 と書いてしまうと、インスタンスごとに別の変数ができてしまい、合計を数える本来の目的が果たせなくなります。

Product.x += 1 と self.x += 1 の違い
Product.x+= 1クラスの x3 まで増える正しく累計が取れるself.x+= 1各自に新しい x が誕生クラスの x は0 のまま更新結果代入結果
クラス経由なら 同じ 1 つのカウンタ を全員で増やせる。self.x += 1 だと各自が自分の中にコピーを作ってしまい、共有のカウンタは 0 のまま。
class Product:
    created_count = 0          # クラス変数:累計商品数

    def __init__(self, name):
        self.name = name
        Product.created_count += 1   # クラス経由で更新

apple = Product("りんご")
banana = Product("バナナ")
orange = Product("みかん")

print(Product.created_count)  # 3
print(apple.created_count)    # 3(インスタンス経由でも同じ値が読める)

クラス変数を インスタンス生成のたびに 1 ずつ増やす カウンターを作ります。

class Product: の中にクラス変数 created_count = 0 を書いてください。

__init__(self, name)self.name = name を代入したあと、Product.created_count += 1 を 1 行で書いてください。

Product("りんご") / Product("バナナ") / Product("みかん") を順に作り、最後に print(Product.created_count)3 が表示されることを確認してください。

Python エディタ

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

今回は、クラス変数とインスタンス変数の 2 種類 を整理し、self.x = ... を書くと毎回新しいインスタンス変数が作られる こと、インスタンス変数があればクラス変数より優先される こと、そして全員で共有したい値はクラス経由で更新することを確認しました。

QUIZ

理解度チェック

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

Q1クラス変数 はどこに書きますか?

Q2次のコードを実行したとき、最後の print の出力はどれですか?
class P:
n = 10
a = P()
a.n = 99
b = P()
print(b.n)

Q3クラス変数 count全インスタンスから共通でカウントアップ したいとき、__init__ の中で書くべきはどれですか?