元ネタはこちらの記事です。
お題:
以下のようなクラスを定義しています。
class TestUnicode(unicode): def __init__(self, x): print x
このクラスは、キーワードを指定しないとインスタンスを作成できますが、
キーワードを指定してインスタンスを作成するとエラーになってしまうようです。
>>> a = TestUnicode('a') a >>> a u'a' >>> b = TestUnicode(x='b') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'x' is an invalid keyword argument for this function
なぜキーワードの指定の有無で挙動が変わってしまうのでしょうか?
答え:
unicode.__new__が、xというキーワード引数を受け取れないことが原因。
TestUnicode(x='a')とやった場合、
まず、TestUnicode.__new__(cls, x='a')が実行され、
その後で、TestUnicode.__init__(self, x='a')が実行されます。
(http://www.python.jp/doc/release/reference/datamodel.html#object.__new__)
ここで、TestUnicode.__new__は定義されていないので、
unicode.__new__が呼ばれることになりますが、
unicode.__new__の引数の定義は「string [, encoding[, errors]]」です。
つまり
unicode.__new__をオーバーライドしない限り、xというキーワードはダメで、stringを指定しなくてはいけない。
__init__と__new__の引数定義は揃えなくてはならない。
というわけです
したがって最初の例は、こんな感じに置き換えられます。
>>> class TestUnicode2(unicode): ... def __init__(self, string, *vals, **kwds): ... print string ... >>> b = TestUnicode2(string = 'b') b >>> b u'b'
dictやlistとかだと、__new__は、ほとんど何もしない(いちおう空の辞書やリストは作る)ので、__new__の存在は忘れがちですね。
なるほど、こういうことでしたか。
返信削除実は私自身、自分の結論では__ini__がオーバーライドできないはずなのにキーワードを指定していなければ"print x"が動作する理由がわからないなと思いながら書いたのでやっと納得がいきました。
ありがとうございます。