简体   繁体   English

为什么我不能在python3中子类化元组?

[英]Why can't I subclass tuple in python3?

Let's preface this question by saying that you should use __new__ instead of __init__ for subclassing immutable objects . 让我们先说这个问题, 你应该使用__new__而不是__init____new__不可变对象

With that being said, let's see the following code: 话虽如此,让我们看看以下代码:

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

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

This works in python2, but in python3 I get: 这适用于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

Why does this happen? 为什么会这样? What changed in python3? python3有什么变化?

Python 3 changed how object.__new__ and object.__init__ react to arguments when both are overridden. Python 3改变了object.__new__object.__init__如何在重写时对参数作出反应。 If a class overrides (or inherits methods that override) both object.__init__ and object.__new__ , object.__init__ and object.__new__ will throw an exception if they receive any excess arguments. 如果一个类重写(或继承覆盖的方法) object.__init__object.__new__object.__init__object.__new__如果收到任何多余的参数,将抛出异常。 In Python 2, that would have given a DeprecationWarning (suppressed by default). 在Python 2中,这将给出DeprecationWarning(默认情况下被禁止)。

tuple doesn't have its own __init__ . tuple没有自己的__init__ It inherits object.__init__ , so you're actually passing a bunch of arguments to object.__init__ that object.__init__ doesn't take. 它继承了object.__init__ ,所以你实际上是将一堆参数传递给object.__init__那个object.__init__没有。 Python 2 was giving you a (suppressed) warning, and Python 3 is making it an error. Python 2给你一个(压制)警告,而Python 3正在使它成为一个错误。

The code has a comment that does a good job of explaining object.__init__ and object.__new__ 's subtle handling of extra arguments: 代码有一个注释,可以很好地解释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.

*/

I've been digging around the C code-base and I haven't found any real clues there (yet) about what changed to disallow this behavior in python3. 我一直在挖掘C代码库,我还没有找到任何关于在python3中禁止这种行为的改变的真正线索。 I've tested on python2.7, python3.3, python3.5 and python3.6. 我已经在python2.7,python3.3,python3.5和python3.6上测试过了。 The only time your code works without an exception is on python2.7. 你的代码唯一没有异常的工作是在python2.7上。 I also haven't found any references in the documentation about why this changed however, I do have some ideas... 我还没有在文档中找到任何关于为什么会改变的引用,但我确实有一些想法......

First, let's agree that tuple.__init__ cannot do anything since tuple are immutable. 首先,我们同意tuple.__init__不能做任何事情,因为tuple是不可变的。 By the time __init__ is called, the tuple is already frozen. __init__被调用时,元组已经被冻结。 So, this leads us to my guess -- since tuple.__init__ does nothing, the devs considered it misleading to allow it to accept any arguments at all. 因此,这引出了我的猜测 - 因为tuple.__init__什么都不做,开发人员认为它误导了它允许它接受任何参数。 By preventing the base-class from accepting arguments, they encourage people to override __new__ (and therefore, encourage proper inheritance for immutable objects). 通过阻止基类接受参数,它们鼓励人们覆盖__new__ (因此,鼓励对不可变对象进行适当的继承)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM