为什么我不能在 python3 中子类化元组?
Why can't I subclass tuple in python3?
让我们先说这个问题 you should use __new__
instead of __init__
for subclassing immutable objects.
话虽如此,让我们看下面的代码:
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 发生了什么变化?
我一直在研究 C 代码库,但我还没有找到任何关于 python3 中禁止这种行为的改变的真正线索。我已经在 python2.7、python3.3、python3.5 和 python3.6 上进行了测试。您的代码唯一一次无异常运行是在 python2.7。我也没有在文档中找到任何关于为什么会发生变化的参考资料,但是,我确实有一些想法...
首先,我们同意 tuple.__init__
不能做任何事情,因为 tuple
是不可变的。调用 __init__
时,元组已经冻结。所以,这让我们得出了我的猜测——因为 tuple.__init__
什么都不做,开发人员认为允许它接受任何参数是误导性的。通过阻止 base-class 接受参数,他们鼓励人们覆盖 __new__
(因此,鼓励对不可变对象进行适当的继承)。
Python 3 更改了 object.__new__
和 object.__init__
在两者都被覆盖时对参数的反应。如果 class 覆盖(或继承覆盖的方法)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.
*/
让我们先说这个问题 you should use __new__
instead of __init__
for subclassing immutable objects.
话虽如此,让我们看下面的代码:
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 发生了什么变化?
我一直在研究 C 代码库,但我还没有找到任何关于 python3 中禁止这种行为的改变的真正线索。我已经在 python2.7、python3.3、python3.5 和 python3.6 上进行了测试。您的代码唯一一次无异常运行是在 python2.7。我也没有在文档中找到任何关于为什么会发生变化的参考资料,但是,我确实有一些想法...
首先,我们同意 tuple.__init__
不能做任何事情,因为 tuple
是不可变的。调用 __init__
时,元组已经冻结。所以,这让我们得出了我的猜测——因为 tuple.__init__
什么都不做,开发人员认为允许它接受任何参数是误导性的。通过阻止 base-class 接受参数,他们鼓励人们覆盖 __new__
(因此,鼓励对不可变对象进行适当的继承)。
Python 3 更改了 object.__new__
和 object.__init__
在两者都被覆盖时对参数的反应。如果 class 覆盖(或继承覆盖的方法)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.
*/