このページではクラス変数について解説します。
(※この記事は入門編のクラス変数に関する既存の記事を切り出し加筆したものになります。)
クラス変数とは
Pythonのクラスにはclass文直下に変数を定義することが可能で、これをクラス変数と呼びます。後述しますがインスタンスの共通値や静的な値をクラスに属させるような使い方をします。
クラス変数1
クラス変数2
:
また、クラス変数はクラス名と変数名をドットでつないで参照することが可能です。
例をみてみましょう。
class Coordinate:
x = 0
y = 0
print(Coordinate.x, Coordinate.y) # 0 0
上のサンプルでは、平面座標を表すクラスCoordinateについて、クラス変数x, yが実装されています。それぞれの値をprint関数で確認しています。
クラス変数とインスタンス化
ここから少し難しくなります。クラスをインスタンス化してもクラス変数にアクセスできるのですが、インスタンスにクラス変数と同じ名前の変数で代入すると属性が新たに上書きされ、インスタンスからはクラス変数にアクセスできなくなります。挙動が複雑なので注意してください。
class Sample:
val = []
# インスタンスを2つ生成
s1 = Sample()
s2 = Sample()
# valを更新
s1.val.append(100)
s2.val.append(200)
# すべて同じクラス変数を参照しているので同じ結果が出力される
print(Sample.val) # [100, 200]
print(s1.val) # [100, 200]
print(s2.val) # [100, 200]
# s2に対してクラス変数と同じ変数を設定してみる
s2.val = []
s2.val.append(300)
# s2はインスタンス変数を参照している
print(Sample.val) # [100, 200]
print(s1.val) # [100, 200]
print(s2.val) # [300]
上のコードでは、リストをクラス変数としてもつクラスSampleを定義しています。生成したインスタンスからもクラス変数が参照できていることが確認てきます。一方で、代入することにより新たにインスタンス変数が設定されています。こういった性質から、通常クラス変数をインスタンスからも参照する場合はイミュータブルなものを使用します。
クラス変数の使い所
クラス変数は以下の用途で使用することが多いかと思います。
共通初期値
先程の解説の通り、インスタンスを生成するとインスタンスを介してクラス変数にアクセスできます。このため、インスタンスに所定の属性と初期値が設定されのと同じように扱うことができます。ただし、前述の通り参照先はインスタンス変数ではなくクラス変数であるため、初期値はイミュータブルなものが望ましいです。
静的な値をクラスに属させる
もう1つの使い方として静的な定数値をクラスに属させたい場合に使用します。以下は単価と数量をもつ注文を表すクラスですが、消費税を定数としてクラス変数に持っています。
class Order:
""" 注文クラス """
CONSUMPTION_TAX = 0.1
def __init__(self, unit_price, quantity):
self.unit_price = unit_price
self.quantity = quantity
def calc_total_price(self):
total_price = (self.unit_price * self.quantity) * (1 + Order.CONSUMPTION_TAX)
return total_price
# インスタンスを生成して処理をする
o = Order(100, 10)
print(o.calc_total_price())
# 定数を取り出して利用する場合
print(Order.CONSUMPTION_TAX)
インスタンス化して処理する以外にクラスが管理する定数として使用したい場合に使用します。
クラス変数の注意点
色々癖の多いクラス変数ですが、最後にもう1つ注意点があります。クラス変数もインスタンスの変数のように更新可能なのですが、インスタンス化した際に参照されてしまう共通変数であるため、更新すると全てのインスタンスから参照した値が切り替わります。
class Sample:
val = 100
s1 = Sample()
print(s1.val) # 100
Sample.val = 200
s2 = Sample()
print(s2.val) # 200
print(s1.val) # 200
上のサンプルはクラス変数を更新していますが、インスタンス化のタイミングに依らず変数の値が変わっています。個人的にはクラス変数を更新するような実装は避けるようにしています。
