クラス変数は、インスタンス属性によって隠されます。これは、属性を検索するときに、Python が最初にインスタンスを検索し、次にクラスを検索することを意味します。さらに、オブジェクトに変数を設定すると (例: self
)、常にインスタンス変数が作成されます。クラス変数が変更されることはありません。
これは、2番目の例で次のことを行う場合を意味します。
self.x += 1
これは (この場合、脚注を参照) 以下と同等です。
self.x = self.x + 1
Python が行うことは次のとおりです。
- 見上げる
self.x
。その時点で、 にself
は instance 属性がないx
ため、値が の class 属性A.x
が見つかります10
。
- RHS が評価され、結果が得られ
11
ます。
x
この結果は の新しいインスタンス属性に割り当てられますself
。
その下で を検索するx.x
と、 で作成されたこの新しいインスタンス アトリビュートが得られますadd()
。を検索するy.x
と、クラス属性が取得されます。クラス属性を変更するには、A.x += 1
明示的に使用する必要があります。ルックアップは、属性の値を読み取るときにのみ行われます。
あなたの最初の例は古典的な問題であり、クラス属性をインスタンス属性の「デフォルト」値として使用すべきではない理由です。電話すると:
self.x.append(1)
行われる割り当てはありませんself.x
。( のような可変オブジェクトの内容list
を変更することは、代入と同じではありません。) したがって、それを隠してしまうような新しいインスタンス属性が追加されるx
ことはなく、調べてx.x
後でy.x
クラス属性から同じリストを取得します。
注: Python では、x += y
常に と同等であるとは限りませんx = x + y
。Python では、型の通常の演算子とは別にインプレース演算子をオーバーライドできます。これは、インプレース バージョンが式の LHS を再割り当てせずにコンテンツを直接変更する可変オブジェクトの場合にほとんど意味があります。ただし、不変オブジェクト (2 番目の例の数値など) は、インプレース演算子をオーバーライドしません。その場合、ステートメントは通常の追加と再割り当てとして評価され、表示される動作を説明します。
(私はこのSO回答から上記を持ち上げました。詳細についてはそこを参照してください。)