私は最近Pythonのバグと戦いました。それはそれらの愚かな初心者のバグの1つでしたが、Python(私はPythonに慣れていないC++プログラマーです)のメカニズムについて考えるようになりました。バグのあるコードをレイアウトしますそして、私がそれを修正するために何をしたかを説明してから、いくつか質問があります...
シナリオ:Aという名前のクラスがあり、そのクラスには辞書データメンバーがあり、そのコードは次のとおりです(これはもちろん単純化です)。
_class A:
dict1={}
def add_stuff_to_1(self, k, v):
self.dict1[k]=v
def print_stuff(self):
print(self.dict1)
_
このコードを使用するクラスはクラスBです。
_class B:
def do_something_with_a1(self):
a_instance = A()
a_instance.print_stuff()
a_instance.add_stuff_to_1('a', 1)
a_instance.add_stuff_to_1('b', 2)
a_instance.print_stuff()
def do_something_with_a2(self):
a_instance = A()
a_instance.print_stuff()
a_instance.add_stuff_to_1('c', 1)
a_instance.add_stuff_to_1('d', 2)
a_instance.print_stuff()
def do_something_with_a3(self):
a_instance = A()
a_instance.print_stuff()
a_instance.add_stuff_to_1('e', 1)
a_instance.add_stuff_to_1('f', 2)
a_instance.print_stuff()
def __init__(self):
self.do_something_with_a1()
print("---")
self.do_something_with_a2()
print("---")
self.do_something_with_a3()
_
do_something_with_aX()
を呼び出すたびに、クラスAの新しい「クリーン」インスタンスが初期化され、追加の前後に辞書が出力されることに注意してください。
バグ(まだ理解していない場合):
_>>> b_instance = B()
{}
{'a': 1, 'b': 2}
---
{'a': 1, 'b': 2}
{'a': 1, 'c': 1, 'b': 2, 'd': 2}
---
{'a': 1, 'c': 1, 'b': 2, 'd': 2}
{'a': 1, 'c': 1, 'b': 2, 'e': 1, 'd': 2, 'f': 2}
_
クラスAの2回目の初期化では、辞書は空ではありませんが、最後の初期化の内容から始まります。私は彼らが「新鮮」に始めることを期待していました。
この「バグ」を解決するのは明らかに次を追加することです。
_self.dict1 = {}
_
クラスAの___init__
_コンストラクターで。
編集:回答に続いて、データメンバーを宣言し、___init__
_または他の場所でself.dict1として参照しないことで、C++/Javaで静的データメンバーと呼ばれるものを実際に定義していることを理解しています。 self.dict1と呼ぶことで、「インスタンスにバインド」しています。
バグとして言及し続けるのは、 文書化された 、Pythonクラスの標準的な動作です。
最初に行ったように__init__
の外でdictを宣言することは、クラスレベルの変数を宣言することです。新しいオブジェクトを作成するたびに、この同じ辞書を再利用します。インスタンス変数を作成するには、__init__
のself
で宣言します。それと同じくらい簡単です。
@Matthew:オブジェクト指向プログラミングのクラスメンバーとオブジェクトメンバーの違いを確認してください。この問題は、元の辞書の宣言がオブジェクトメンバーではなくクラスメンバーになるために発生します(元の投稿者の意図どおり)。したがって、クラスのすべてのインスタンスに対して1回(共有)存在します(つまり1回)クラス自体の場合は、クラスオブジェクト自体のメンバーとして)、動作は完全に正しいです。
インスタンスの属性、たとえばself.fooにアクセスすると、pythonは最初にself.__dict__
で 'foo'を見つけます。見つからない場合は、python TheClass.__dict__
で 'foo'が見つかります
あなたの場合、dict1
はインスタンスではなくクラスAです。
これがあなたのコードの場合:
class ClassA:
dict1 = {}
a = ClassA()
次に、おそらくこれはPython内で起こると予想していました。
class ClassA:
__defaults__['dict1'] = {}
a = instance(ClassA)
# a bit of pseudo-code here:
for name, value in ClassA.__defaults__:
a.<name> = value
私が知る限り、isは何が起こるかです。ただし、dict
の値の代わりにポインターがコピーされます。これはPythonのどこでもデフォルトの動作です。このコードを見てください:
a = {}
b = a
a['foo'] = 'bar'
print b
Pythonクラス宣言はコードブロックとして実行され、ローカル変数定義(その関数定義は特別な種類です)は構築されたクラスインスタンスに格納されます。 Pythonでの属性検索の仕組みにより、インスタンスで属性が見つからない場合、クラスの値が使用されます。
クラス構文に関する興味深い記事 Python blog。