class
をpythonで宣言する際に、クラス内変数を宣言することがあります。
特にクラス内で値を追加したり削除したりするリストを使いたい場面がよくあります。
クラス内変数の宣言方法や「クラス内変数」という呼び方が実は曖昧な呼び方であったことを知ったので備忘録としてまとめていきます。
- 一般的に、インスタンス変数はそれぞれのインスタンスについて固有のデータのためのもので、クラス変数はそのクラスのすべてのインスタンスによって共有される属性やメソッドのためのものです:
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind # shared by all dogs
'canine'
>>> e.kind # shared by all dogs
'canine'
>>> d.name # unique to d
'Fido'
>>> e.name # unique to e
'Buddy'
同じクラス内で使う変数でも「クラス変数」と「インスタンス変数」が区別されています。
- クラス変数:全てのインスタンスによって共有される属性。
- インスタンス変数:1つのインスタンス固有の属性。
すなわち、コンストラクタ以下で宣言する変数と
class ***
以下で宣言する変数が異なるということです。
特にクラス変数で宣言してしまうと、全てのインスタンス(ここ大事)で変数が共有されているので、リストや辞書などの値を追加・保持する系のオブジェクトを使用する際には特に注意が必要です。
公式ドキュメントでは、以下のソースコードのようにクラス変数としてlist型objectを宣言すべきでないとしています。
class Dog:
tricks = [] # mistaken use of a class variable
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
上記の例では、クラス変数として宣言したlist型変数tricks
について、2つの異なるインスタンスd
とe
にadd_trick
された値が2つのインスタンスd
とe
で共有されていることわかります。
すなわち、クラス変数は、インスタンスが異なっても変数を共有します。
予期しない動作を防ぐためにも、list型などmutableなオブジェクトはインスタンス変数で使用しましょう。