用多个__init__参数对元组进行子类化

62 投票
2 回答
29262 浏览
提问于 2025-04-15 15:03

下面的代码可以正常运行:

class Foo(tuple):

    def __init__(self, b):
        super(Foo, self).__init__(tuple(b))

if __name__ == '__main__':
    print Foo([3, 4])

$ python play.py 

结果是:

play.py:4: DeprecationWarning: object.__init__() takes no parameters
  super(Foo, self).__init__(tuple(b))
(3, 4)

但是下面的代码就不行:

class Foo(tuple):

    def __init__(self, a, b):
        super(Foo, self).__init__(tuple(b))

if __name__ == '__main__':
    print Foo(None, [3, 4])

$ python play.py 

结果是:

Traceback (most recent call last):
  File "play.py", line 7, in <module>
    print Foo(None, [3, 4])
TypeError: tuple() takes at most 1 argument (2 given)

这是为什么呢?

2 个回答

64

要给元组赋值,你需要重写一下 __new__ 方法:

class Foo(tuple):

    def __new__ (cls, a, b):
        return super(Foo, cls).__new__(cls, tuple(b))

看起来元组类的 __init__ 方法会忽略传入的参数,但如果你需要做一些初始化的工作,可以这样做:

class Foo(tuple):

    def __new__ (cls, a, b):
        return super(Foo, cls).__new__(cls, tuple(b))

    def __init__(self, a, b):
        self.a=a
        self.b=b

if __name__ == '__main__':
    foo = Foo(None, [3, 4])
    print foo
    print foo.a
    print foo.b
77

因为元组是不可变的,所以你需要重写 __new__ 方法:

查看 Python 文档

object.__new__(cls[, ...])

这个方法是用来创建一个新类 cls 的实例的。__new__() 是一个静态方法(特别处理过,所以你不需要特别声明它),它的第一个参数是你想要创建实例的类。后面的参数是传给对象构造函数的(也就是调用类时传入的参数)。__new__() 的返回值应该是新创建的对象实例(通常是 cls 的一个实例)。

一般的实现方式是通过调用父类的 __new__() 方法来创建一个新实例,使用 super(currentclass, cls).__new__(cls[, ...]) 并传入合适的参数,然后在返回之前根据需要修改这个新创建的实例。

如果 __new__() 返回的是 cls 的实例,那么新实例的 __init__() 方法会被调用,像这样 __init__(self[, ...]),其中 self 是新实例,后面的参数和传给 __new__() 的参数是一样的。

如果 __new__() 没有返回 cls 的实例,那么新实例的 __init__() 方法就不会被调用。

__new__() 主要是为了让不可变类型的子类(比如 intstrtuple)能够自定义实例的创建。它也常常在自定义元类中被重写,以便自定义类的创建过程。

撰写回答