如何(或为何不)从子类调用unicode.__init__

2 投票
1 回答
1369 浏览
提问于 2025-04-17 15:18

我遇到了一个情况,就是在Python 3.3之前,继承unicode会出现弃用警告,而在Python 3.3上则会出错:

# prove that unicode.__init__ accepts parameters
s = unicode('foo')
s.__init__('foo')
unicode.__init__(s, 'foo')

class unicode2(unicode):
    def __init__(self, other):
        super(unicode2, self).__init__(other)

s = unicode2('foo')

class unicode3(unicode):
    def __init__(self, other):
        unicode.__init__(self, other)

s = unicode3('foo')

有趣的是,警告和错误并不是出现在前面三行,而是在第8行和第14行。这是Python 2.7的输出结果。

> python -Wd .\init.py
.\init.py:8: DeprecationWarning: object.__init__() takes no parameters
  super(unicode2, self).__init__(other)
.\init.py:14: DeprecationWarning: object.__init__() takes no parameters
  unicode.__init__(self, other)

这个代码是简化过的,目的是为了说明这个问题。在实际应用中,我会做的不止是简单地调用父类的 __init__ 方法。

从前面三行看,unicode类实现了 __init__ 方法,并且这个方法至少需要一个参数。但是,如果我想从子类中调用这个方法,不管我是否使用 super(),似乎都无法成功。

为什么在unicode实例上调用 unicode.__init__ 是可以的,但在unicode子类上却不行?如果要继承unicode类,作者应该怎么做呢?

1 个回答

4

我怀疑问题出在unicode是不可变的这一点上。

一旦创建了一个unicode实例,它就不能被修改。所以,任何初始化的逻辑都应该放在__new__方法里(这个方法是用来创建实例的),而不是__init__(这个方法只有在实例存在后才会被调用)。

对于不可变类型的子类来说,要求没有那么严格,所以如果你愿意,可以在unicode2.__init__里做一些事情,但调用unicode.__init__其实是没必要的(而且可能不会像你想的那样工作)。

一个更好的解决方案可能是在你自己的__new__方法里实现自定义逻辑:

class unicode2(unicode):
    def __new__(cls, value):
        # optionally do stuff to value here
        self = super(unicode2, cls).__new__(cls, value)
        # optionally do stuff to self here
        return self

如果你想的话,也可以让你的类变得不可变,方法是给它一个__setattr__方法,这个方法总是抛出异常(你可能还想给这个类一个__slots__属性,这样可以节省内存,因为可以省去每个实例的__dict__)。

撰写回答