2023年6月11日、色々と調べてくださった方の情報を追記しました。それをみてもらえれば「copy-on-write」というのはやはり的外れでした。ので、タイトルも若干修正。
(2023年6月17日。「shadow-on-assign」はChatGPT命名だそうです→ https://twitter.com/sumim/status/1667680973960654849 )
オライリーの 入門Python3 (分厚い奴ね:-)と以下の記事を見て???と思ったので実験。
記事: Python クラス変数 と インスタンス変数 の違い https://aiacademy.jp/media/?p=922
テストしたコードはこれ。
class Z:実行した結果。
y = 'a'
a = Z()
b = Z()
print(f"Z.y = {Z.y}")
print(f"a.y = {a.y}")
print(f"b.y = {b.y}")
a.y = 'x'
print(f"Z.y = {Z.y}")
print(f"a.y = {a.y}")
print(f"b.y = {b.y}")
Z.y = 'q'
print(f"Z.y = {Z.y}")
print(f"a.y = {a.y}")
print(f"b.y = {b.y}")
a.__class__.y = 'r'
print(f"Z.y = {Z.y}")
print(f"a.y = {a.y}")
print(f"b.y = {b.y}")
Z.y = a
a.y = a
b.y = a
Z.y = a
a.y = x
b.y = a
Z.y = q
a.y = x
b.y = q
Z.y = r
a.y = x
b.y = r
つまり、クラス属性はインスタンス生成時には、そのインスタンスはクラス属性を共有しているが、その値を変更(write)する際には、インスタンス属性としてcopy-on-writeするということでしょうかねぇ。。。
ちょっとcopy-on-writeのところって、分かりにくくないか?。むしろ上記の例なら「a.y = 」で書き換えるのをエラーか何かにしてしまった方がいいんじゃないだろうか?。
勿論、言語の設計はその言語の設計者の自由なんだけど。なんか、これでうっかりして挙動に頭を抱えている人がいないか心配してしまった:-)
2023年6月11日追記。
この件で以下のようにツイートしたところ
簡単なコードで実験の結果、こういう解釈ではないかと。。これはわかりにくい気がした(人並みの感想:-)
— Tetsuo Sakaguchi (@tsaka1) June 7, 2023
RT: tsaka1's blog: Python3 のクラス属性はcopy-on-writeでインスタンス属性になるのか? https://t.co/xg1JRwgYfa
@sumim さんが色々と調べてくださって、他の方からの情報もあって、結局以下のような仕様だということがわかりました。関わった皆様に感謝です。
分かりました。
— sumim (@sumim) June 8, 2023
つまり、インスタンスに限らず、Pythonでは移譲先の属性を再代入することはできない(shadow-on-assign ^^; になる)仕様なのでした。https://t.co/7lGuX20e09
まぁ、この仕様については、
おお、ありがとうございます。(私へのメンションがなかったので、話が続いていた事に気づいてませんでした。。)
— Tetsuo Sakaguchi (@tsaka1) June 10, 2023
仕様がわかってスッキリしました。皆様感謝です。
(が、Smalltalk-80の「1 + 2 * 3」の解釈同様に考え方はわかるけど、、という感想は別途残りますがwww)https://t.co/mZ0BQy7yEJ
にも触れたように、若干わかりにくいかなという感想は残りました。雑なたとえですが、Common Lisp以前のLispで普通だったDynamic Scope を連想してしまいましたし(いや、全然違いますけどね:-)