Python のクラス変数とインスタンス変数の危ない関係

Python でクラス変数を書き換えると生成済みのインスタンス変数にビックリな影響を与えることを知ったので、それについて書き留めておきます。使ったのは Python 3.5 です。

class Sandwich:
    ham = 0

メンバに変数 ham を持つ Sandwitch クラスを用意しました。このクラスのインスタンスを生成します。

>>> sw = Sandwich()
>>> sw.ham # インスタンス変数
0

当然、インスタンス変数は初期化された値が入ります。ではクラス変数を別の値に書き換えるとどうなると思いますか?

>>> Sandwich.ham = 1 # クラス変数に別の値を代入

そうすると…

>>> sw.ham # インスタンス変数
1

!?!?!? ٩(๑òωó๑)۶ wtf !?!?!?

予想外の結果ではないでしょうか? ぼくはまったく予想外でした。クラス変数の値の書き換えるとインスタンス変数の値も一緒に書き換わります。つまりインスタンス変数はクラス変数の参照なんですね。では、インスタンス変数に値を代入するとクラス変数はどうなるのか。

>>> sw.ham = 2
>>> Sandwich.ham
1

インスタンス変数を書き換えても、クラス変数は書き換わりません。ではもう一度、クラス変数を書き換えてみます。

>>> Sandwich.ham = 3
>>> sw.ham
2

書き換わりません。一度値を書き換えられたインスタンス変数は、クラス変数を書き換えても影響はありません。

要点をまとめるとこうです。

  • インスタンス生成時点ではインスタンス変数はクラス変数への参照。
  • このときクラス変数を書き換えるとインスタンス変数も書き換わる。
  • インスタンス変数は書き換えられるとクラス変数への参照が外れる。
  • それ以降はクラス変数の書き換えに影響を受けなくなる。

なんで Python がこんな仕様なのかよくわかりませんが、気をつけないと見つけにくいバグになりそうです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*