クラス変数とインスタンス変数(アトリビュート)
クラス変数
クラスの内部 (スイート) に定義される変数を クラス変数 とよびます。
クラス変数はクラス自身がもつ属性値であり、すべてのインスタンスに共有されます。
たとえば、次のような単純なクラスを設計してみます。
# In[1]
class My_class:
x = 100
クラス変数 x はクラス自身がもつ情報なので、クラスの属性値として参照できます。
# In[2]
# My_classのクラス変数xを参照
print(My_class.x)
100
クラス変数を上書きすることもできます。
# In[3]
# クラス変数を上書きする
My_class.x = 200
# My_classのクラス変数xを参照
print(My_class.x)
200
ただし、クラス変数の上書きはクラスの設計変更にあたり、その後生成されるすべてのインスタンスが影響を受けます。原則として設計変更はクラスの内部 (スイート) を修正して対応すべきです。特にクラスの定義文とは全く関係ない箇所でそのようなコードを書き加えてしまうとコードの可読性は著しく損なわれます。他人があなたのコードを読むとき、クラスの設計内容はクラスの定義文にまとまっていると思っています。クラス変数を外から書き換えるような方法はなるべく避けてください。
クラス変数は個々のインスタンスから参照することもできます。
# In[4]
# My_classのインスタンスを作成
My_object = My_class()
# クラス変数を参照
print(My_object.x)
100
ただし、インスタンスにクラス変数が保存されているわけではありません(そのようなコピーはメモリの無駄遣いです)。上の参照方法はオブジェクトを介して間接的にクラス変数にアクセスしているだけです。
クラス変数を使用することの利点は、グローバル変数に頼らなくてすむということです。
クラス変数はクラス自身から、あるいはクラスから生成されたインスタンスから簡単に参照できますが、クラスの内側に隔離された変数なので、グローバル変数のように名前の重複によって上書きされることがありません。
インスタンス変数 (アトリビュート)
__init__() に定義される、すなわち個々のインスタンス生成時に与えられる変数を インスタンス変数 (アトリビュート) とよびます。たとえば、次のようなクラスを設計したとします。
# In[1]
class Class_A:
def __init__(self, value):
self.x = value
Class_A のインスタンスは生成時に引数 value に受け取った値をインスタンス変数として内部に保存します。たとえば、value に 100 を渡すとインスタンス変数 100 を保存します。この値はインスタンスの属性値として参照できます。
# In[2]
object_1 = Class_A(100)
# インスタンス変数を参照
print(object_1.x)
100
value に "apple" を渡せばインスタンス変数 "apple" を保存します。
# In[3]
object_2 = Class_A("apple")
# インスタンス変数を参照
print(object_2.x)
apple
インスタンス変数は外部から書き換えることができます。
# In[4]
# インスタンス変数xを上書きする
object_2.x = "orange"
# インスタンス変数を参照
print(object_2.x)
orange
インスタンス変数の追加も可能です。
# In[5]
# インスタンス変数yを追加する
object_2.y = "banana"
# インスタンス変数yを参照
print(object_2.y)
banana
Circlesクラス
円のデータを生成するクラスを設計してみましょう。数学で「円」に分類される図形はすべて相似なので、半径を与えるだけでその特徴が定まります。つまり、ある円を他の円と区別する情報は半径だけです。しかし、半径を与えるだけでは円としての情報は不十分です。良く知られているように、個々の円の面積や周長などを計算するには、すべての円が共通してもつ「円周率の値」という情報が必要不可欠です。そこで、すべての円が共通してもつ円周率の値をクラス変数、個々の円の半径をインスタンス変数としてクラスを設計してみます。
# In[1]
# 円クラスを定義
class Circles:
# 円周率の近似値をクラス変数として定義
pi = 3.141592653589793
# 半径,面積,周長をインスタンス変数として定義
def __init__(self, r):
self.radius = r
self.area =self.pi * self.radius ** 2
self.perimeter = 2 * self.pi * self.radius
円周率 pi をクラス変数として定義して、__init__() の内部で半径 (radius)、面積 (area)、周長 (perimeter) を計算するのに用いています。半径 5 の円を生成して、クラス変数とインスタンス変数を表示してみます。
# In[2]
# Circlesクラスのインスタンスを生成
c1 = Circles(5)
# クラス変数を参照
print("円周率: {:.5f}".format(c1.pi))
# インスタンス変数を参照
print("半径: {:.3f}".format(c1.radius))
print("面積: {:.3f}".format(c1.area))
print("周長: {:.3f}".format(c1.perimeter))
円周率: 3.14159 半径: 5.000 面積: 78.540 周長: 31.416
データの上書きに関する注意点
すでに述べたように、個々のインスタンスを介してクラス変数を参照できますが、データの書き換えはできません。しかし、一見するとクラス変数を書き換えたように錯覚するので注意が必要です。次のコードを実行してみてください。
# In[1]
class My_class:
x = 100
my_object = My_class()
my_object.x = 200
print(my_object.x)
200
実行結果を見ると、my_object を介してクラス変数 x を 200 に書き換えることに成功したように思えます。しかし、実際はそうではありません。クラス変数を直接参照してみましょう。
# In[2]
# クラス変数を参照
print(My_class.x)
100
クラス変数は元の値 100 のままです。
どうしてこのような結果になたのでしょうか?
実は、my_object.x = 200 は「my_object に新しいインスタンス変数 x を追加して 200 を代入する」ことを意味するコードなのです。つまり、my_object はクラス変数と同名のインスタンス変数を備えるようになったということです。このようなケースでは、もはやインスタンスを介してクラス変数を参照することができません。My_class.x という参照がインスタンス変数 x を素通しできないからです。
インスタンスからクラス変数を参照できるのは、クラス変数と同名インスタンス変数が存在しない場合です。とはいえ、先にも述べたように「外側からクラス変数を変更しない」というマナーを守るべきです。そうすれば、わざわざ複雑なルールを覚える必要はなくなります。
下記は誤植と思われますので、ご確認ください。
前の記事の「クラスとオブジェクト」の説明文で、Type( ) 関数を使うと → type( ) 関数を使うと
恐れ入ります。m(_ _)m
直しておきました。