愚痴です。それがpythonの思想だと言われればそれまでなんですけど……
他言語の例
例えばC++。クラス変数というからにはこういうイメージでした。
#include <cassert>
class Foo{
public:
static int value;
};
int Foo::value = 1;
int main(){
Foo foo1 = Foo();
Foo foo2 = Foo();
assert(Foo::value == 1); // クラスから値を取得
assert(foo1.value == 1); // インスタンスから値を取得
Foo::value = 2; // クラスから値を設定
assert(Foo::value == 2);
assert(foo1.value == 2);
foo2.value = 3; // インスタンスから値を設定
assert(Foo::value == 3);
assert(foo1.value == 3);
}
pythonに明るい皆さんにおかれましては、もうオチが見えたことでしょう。
pythonの場合
同じようにやってみます。
class Foo(object):
value: int = 1 # クラス変数
foo1 = Foo()
foo2 = Foo()
assert Foo.value == 1, f'{Foo.value=}' # クラスから値を取得
assert foo1.value == 1, f'{foo1.value=}' # インスタンスから値を取得
Foo.value = 2 # クラスから値を設定
assert Foo.value == 2, f'{Foo.value=}'
assert foo1.value == 2, f'{foo1.value=}'
うんうん、良さそうですね。するとこれも、
foo2.value = 3 # インスタンスから値を設定
assert Foo.value == 3, f'{Foo.value=}'
assert foo1.value == 3, f'{foo1.value=}'
AssertionError Traceback (most recent call last)
Cell In[2], line 2
1 foo2.value = 3 # インスタンスから値を設定
----> 2 assert Foo.value == 3, f'{Foo.value=}'
3 assert foo1.value == 3, f'{foo1.value=}'
AssertionError: Foo.value=2
えっ
何が起きている
value
はクラス変数なのでインスタンスの中にはない筈ですが、覗いてみるとなにやらfoo2
はvalue
を持っています。
print(vars(foo1)) # {}
print(vars(foo2)) # {'value': 3}
それもそのはず、実はこの行はクラス変数を書き換えているのではなく同名のインスタンス変数value
を新規に設定しているんですね。
foo2.value = 3
何が嫌
いや、だって凄く紛らわしくないですか……?
Foo.value # クラス変数へのアクセス
Foo.value = 2 # クラス変数へのアクセス
foo2.value # クラス変数へのアクセス
foo2.value = 3 # インスタンス変数へのアクセス
foo2.value # インスタンス変数へのアクセス
どうしてこんな設計なのかとCopilotに訊くと、意図せず(インスタンス変数のつもりで)クラス変数を書き換えたりしないよう、安全策でこうなっているという説明。でもWe are all consenting adults hereだというなら、触ろうとしているのがクラス変数なのかインスタンス変数なのは理解している筈で、そんな安全策を設ける必要はないのではという気もします。
また、その割にインスタンスからのクラス変数の取得は許しているのも一貫性がないような。これだって、迂闊にインスタンス変数のつもりでクラス変数を読んだりなんかしていたら、思わぬところで値が書き換わっていて……となりそうなものですけれど。
個人的には、それならインスタンスを経由したクラス変数へのアクセスは読み書きともに禁止した方が良かったのではないかと思ってしまいます。型アノテーションでクラス名は分かるし、というのは近年のpythonに染まった考え方かもしれませんが、そうでなくともtype(instance).class_var
とかすればいいわけですし。
……なんて言っている間に手を動かしてコードを書いていればいずれ慣れて気にならなくなるんでしょうけどね。どっとはらい。