ITエンジニアのブログ

IT企業でエンジニアやってる人間の日常について

Python3 コンストラクタ__init__ の初期化の値について

Python3 で Wikipedia のデータを解析するために、

class Entry:
    def __init__(self, title, redirect_from=[], document=None, link_from=[], link_to=None):
        self.title = title
        self.redirect_from = redirect_from
        self.document = document
        self.link_from = link_from
        self.link_to = link_to

というクラスを作ってデータを入力していたのですが、何故か全インスタンスの redirect_from のデータを見ると同じ内容だったので、もしかして同じものが参照されているのかと思って調べてみました。 Python3 のオブジェクト x は id(x) を使って調べることが出来ます。

>>> class A:
...   def __init__(self, a=[]):
...     self.a = a
...
>>> a = A()
>>> b = A()
>>> id(a.a) == id(b.b)
True

どうやらコンストラクタでデフォルト引数にオブジェクトを渡すと、共通した値を使ってしまうようですね。
結局対策として簡単に思いつくのは

class Entry:
    def __init__(self, title, redirect_from=None, document=None, link_from=None, link_to=None):
        self.title = title
        if redirect_from == None:
            self.redirect_from = []
        else:
            self.redirect_from = redirect_from
        self.document = document
        if link_from == None:
            self.link_from = []
        else:
            self.link_from = link_from
        self.link_to = link_to

というように、デフォルト引数に None を指定しておき、引数が None だったら [] を渡すというようにします。これだと None を値に持たせることが出来ませんが、デフォルトを空リストにしたい場合ですから殆どの場面で大丈夫だと思います。あるいはどうしても None を値に持たせたくなればインスタンスを作ったあとに None を代入します。