LoginSignup
4
5

More than 5 years have passed since last update.

Pythonのクラス変数を安全に書き換える

Last updated at Posted at 2018-09-13

前置き

次のようなコードを書いて、予想しない挙動に困っている人をしばしば見る。

class MyClass:
    cls_var_int = 42
    cls_var_str = 'spam'

    def set_cls_vars(self, new_val_int, new_val_str):
        self.cls_var_int = new_val_int
        self.cls_var_str = new_val_str


inst1 = MyClass()
inst2 = MyClass()

inst1.set_cls_vars(6, 'ham')
assert inst1.cls_var_int is inst2.cls_var_int  # AssertionError! なんで!?

クラス変数としてのcls_var_intと、
インスタンス変数としてのそれが共存してしまっているのが原因だ。

>>> inst1.cls_var_int
6
>>> MyClass.cls_var_int
42

このような凡ミスを避ける一つの方法として、
常に「クラス名.属性名」でのアクセスを徹底する、というものがある。

class MyClass:
    cls_var_int = 42
    cls_var_str = 'spam'

    def set_cls_vars(self, new_val_int, new_val_str):
        MyClass.cls_var_int = new_val_int
        MyClass.cls_var_str = new_val_str

しかしこの場合、クラス名の変更に際して被影響範囲が広くなってしまう。

本題

この例に限ってはクラスメソッドを利用すれば解決するのだが、
都合上どうしてもインスタンスメソッド内でクラス変数を書き換えたい場合もある。

そのようなとき、個人的には次のように書いてしまうことが多い。

class MyClass:
    cls_var_int = 42
    cls_var_str = 'spam'

    def set_cls_vars(self, new_val_int, new_val_str):
        cls = type(self)

        cls.cls_var_int = new_val_int
        cls.cls_var_str = new_val_str


inst1 = MyClass()
inst2 = MyClass()

inst1.set_cls_vars(6, 'ham')
assert inst1.cls_var_int is inst2.cls_var_int  # AssertionErrorは起きない

type(self)の代わりにself.__class__を使うと、旧スタイルクラスにも対応できる。
ただ、逆に『2.7に対応させない』ことの方が大事な気もしてくる。

同様のポスト

Qiita - クラス変数にはどうアクセスすべき?

4
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5