python:クラス生成で変数を初期化する場合にハマったこと
もともとC#で開発をしていた私が、Pythonで組み始めたところ、クラス生成の挙動が異なりハマってしまった。
配列は生成時に初期化されない?
試しにこんなコードを書いてみた。
class a: data = 0 ar = [] def addvalue(self): self.data += 1 self.ar.append(self.data) return (self.data) for i in range(0, 3): c = a() c.addvalue() print(str(c.data) + " : " + str(c.ar))
クラスaの定義で、dataとarを初期化している。つまりこれでc=a()とクラスを生成するごとに、dataとarは初期化されるはずである。
このサンプルではクラスaを生成し、addvalue(dataに1を足し、arに追加する)を呼び出し、クラスのdataとarを出力する動作を3回繰り返す。
期待していた出力はこうだ。
1 : [1]
1 : [1]
1 : [1]
ところが結果はこうだった。
1 : [1]
1 : [1, 1]
1 : [1, 1, 1]
クラスを生成するごとに、dataは初期化されているが、arは初期化されていない。これにより、2回目の生成時に、1回目に生成したクラスのarが引き継がれてしまっているのだ。
いや、引き継がれているのか、arがすべて同じ配列を示しているのか?
そこで次のようにコードを変えて再度実行してみる。
car=[] for i in range(0, 3): c = a() c.addvalue() car.append(c) for c in car: print(str(c.data) + " : " + str(c.ar))
結果はこうである。
1 : [1, 1, 1]
1 : [1, 1, 1]
1 : [1, 1, 1]
どうやらすべてのクラスで、arプロパティが同じ配列を指してしまっているようだ。
リストやクラスは__init__で初期化が必要?
そこで、aクラスに__init__コンストラクタを追加し、arを初期化してみた。
class a: data = 0 ar = [] def __init__(self): #コンストラクタ self.ar=[] def addvalue(self): self.data += 1 self.ar.append(self.data) return (self.data) car = [] for i in range(0, 3): c = a() c.addvalue() car.append(c) for c in car: print(str(c.data) + " : " + str(c.ar))
結果はこうなった。期待通りである。
1 : [1]
1 : [1]
1 : [1]
コンストラクタではself指定忘れに注意
ちなみにこの前にもう一つ失敗があった。前のコードのコンストラクタで、ar=[]としたところ、結果が変わらなかった。
なんのことはない、self.ar=[]としなかったため、使われないローカル変数を指定するのみで、クラスのarを初期化できていなかった。
当たり前といえば当たり前なのだが、これ自体は文法的になんの問題もないため、すんなりと実行されてしまう。つまりバグとして見つけにくいのだ。
変数を宣言せずに使える言語は手軽ではあるが、その分バグの発生リスクは高まる。
クラスの初期化については重々注意しておきたい。
-
前の記事
ubuntu20.04でgitサーバーを立てる 2020.06.02
-
次の記事
Python:起動時引数の取得方法 2020.06.10