2023年6月7日水曜日

Python3 のクラス属性はcopy-on-writeでインスタンス属性になるのか?(そうではなくshadow-on-assignというそうです)

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日追記。

この件で以下のようにツイートしたところ

 @sumim さんが色々と調べてくださって、他の方からの情報もあって、結局以下のような仕様だということがわかりました。関わった皆様に感謝です。

まぁ、この仕様については、

 にも触れたように、若干わかりにくいかなという感想は残りました。雑なたとえですが、Common Lisp以前のLispで普通だったDynamic Scope を連想してしまいましたし(いや、全然違いますけどね:-)