2016-12-30 9 views
4

you should use __new__ instead of __init__ for subclassing immutable objectsと言ってこの問題を前に説明します。それは言われているとpython3でタプルをサブクラス化できないのはなぜですか?

、以下のコードを見てみましょう:

class MyTuple(tuple): 
    def __init__(self, *args): 
     super(MyTuple, self).__init__(*args) 

mytuple = MyTuple([1,2,3]) 

これはpython2で動作しますが、のpython3に私が取得:

Traceback (most recent call last): 
    File "tmp.py", line 5, in <module> 
    mytuple = MyTuple([1,2,3]) 
    File "tmp.py", line 3, in __init__ 
    super(MyTuple, self).__init__(*args) 
TypeError: object.__init__() takes no parameters 

これはなぜ起こるのでしょうか? python3で何が変わったのですか?

+0

をあなたは '__new__'を使用していない理由を私は聞いてもいいですか?それは行く方法だと思われる。 – TigerhawkT3

+0

@ TigerhawkT3私は今です:)しかし、私はまだこれについて興味があります – goncalopp

答えて

4

Python 3は、両方がオーバーライドされたときに、どのようにobject.__new__object.__init__が引数に反応するかを変更しました。クラスがオーバーライドする(またはオーバーライドするメソッドを継承する)場合、余分な引数を受け取った場合、object.__init__object.__new__の両方にobject.__init__object.__new__が例外をスローします。 Python 2では、DeprecationWarning(デフォルトでは抑制)が与えられていました。

tupleは、独自の__init__を持っていません。それはobject.__init__を継承しているので、実際にはobject.__init__に多くの引数を渡しています。object.__init__は受け取りません。 Python 2はあなたに(抑制された)警告を与えていました。そしてPython 3はそれをエラーにしています。

コードがobject.__init__と、余分な引数のobject.__new__の微妙な取り扱いを説明するのは良い仕事をしていませんコメントがあります

/* You may wonder why object.__new__() only complains about arguments 
    when object.__init__() is not overridden, and vice versa. 

    Consider the use cases: 

    1. When neither is overridden, we want to hear complaints about 
     excess (i.e., any) arguments, since their presence could 
     indicate there's a bug. 

    2. When defining an Immutable type, we are likely to override only 
     __new__(), since __init__() is called too late to initialize an 
     Immutable object. Since __new__() defines the signature for the 
     type, it would be a pain to have to override __init__() just to 
     stop it from complaining about excess arguments. 

    3. When defining a Mutable type, we are likely to override only 
     __init__(). So here the converse reasoning applies: we don't 
     want to have to override __new__() just to stop it from 
     complaining. 

    4. When __init__() is overridden, and the subclass __init__() calls 
     object.__init__(), the latter should complain about excess 
     arguments; ditto for __new__(). 

    Use cases 2 and 3 make it unattractive to unconditionally check for 
    excess arguments. The best solution that addresses all four use 
    cases is as follows: __init__() complains about excess arguments 
    unless __new__() is overridden and __init__() is not overridden 
    (IOW, if __init__() is overridden or __new__() is not overridden); 
    symmetrically, __new__() complains about excess arguments unless 
    __init__() is overridden and __new__() is not overridden 
    (IOW, if __new__() is overridden or __init__() is not overridden). 

    However, for backwards compatibility, this breaks too much code. 
    Therefore, in 2.6, we'll *warn* about excess arguments when both 
    methods are overridden; for all other cases we'll use the above 
    rules. 

*/ 
+0

私もこのコメントを見ましたが、python2.xコードベースにもあったので、私はそれを十分に読まなかったと思います。 – mgilson

1

私はCコードベースについて掘り下げてきましたが、python3でこの動作を禁止するために何が変わったのかについての本当の手がかりはまだ見つかりませんでした。私はpython2.7、python3.3、python3.5、python3.6についてテストしました。あなたのコードが例外なく動作する唯一の時間は、python2.7です。私はまた、なぜこれが変更されたかについてのドキュメントの参照は見つかりませんでしたが、私はいくつかのアイデアを持っています...

まず、tupleは不変であるので、tuple.__init__は何もできないことに同意しましょう。 __init__が呼び出されるまでに、タプルはすでに凍結されています。だから、これは私の推測につながります.は何もしません。開発者は何の引数も受け入れられないように誤解していると考えました。基底クラスが引数を受け入れないようにすることで、人々は__new__を上書きするよう促します(したがって、不変オブジェクトの適切な継承を推奨します)。

+0

これは良い推測です。興味深いのは、このコードがpython2.7で実際に動作することです。 'print mytuple'は'(1,2,3) 'を返します。 – goncalopp

+0

@goncalopp - もちろんです。それは '__new__'が正しいタプルを作成し、' tuple'に渡された最初の引数とともに '__init__'に渡したからです。 – mgilson

関連する問題