为什么Python不可变类型(如int,str或tuple)需要使用`__new __()`而不只是`__init __()`?

Why do Python immutable types (like int, str, or tuple) need to use `__new__()` instead of just `__init__()`?

这个问题与这个、这个、这个和这个有关,但不是重复的。这些链接不能回答我的问题。不过,这几乎回答了我的问题,但没有,因为答案中的代码没有在python 3.6中运行,而且在任何情况下,我在这里问的问题都没有具体的内容。(见下面我自己的答案。

在python文档页面中,我找到以下文本。

__new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also
commonly overridden in custom metaclasses in order to customize class
creation.

但为什么呢?为什么我们不能直接覆盖__init__(),而不是必须覆盖__new__()?显然,例如,frozenset甚至没有实现__init__();这是为什么?我从这里了解到,在一些罕见的情况下,__new__()__init__()需要做不同的事情,但据我所知,这只是在酸洗和拔料过程中。对于不可变类型,特别是需要使用__new__()而不是__init__()的类型,它是什么?


我是问题操作员,我要回答我自己的问题,因为我想我在打字的中途找到了答案。在别人确认它是正确的之前,我不会把它标为正确的。

这里的这个问题是特别相关的,但是这个问题与这个问题不一样,尽管答案很有启发性(尽管评论变成了关于C和python以及"pythonic"的启发性但深奥的论据),但是应该在这里更清楚地阐述这个问题。我希望这能帮助未来的读者。这个答案中的代码已经在python 3.6.1中得到了验证。

一个不变的对象的问题是,显然,一旦创建了它,就不想设置它的成员。在python中这样做的方法是将__setattr__()特殊方法重写为raise一个错误(AttributeError),这样人们就不能做my_immutable_object.x = 3这样的事情。以下面的自定义不可变类为例。

1
2
3
4
5
6
7
class Immutable(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __setattr__(self, key, value):
        raise AttributeError("LOL nope.")

我们试试看。

1
2
im = Immutable(2, 3)
print(im.a, im.b, sep=",")

输出:

1
AttributeError: LOL nope.

"但是什么!?"我听到你问,"在它被创建之后,我没有设置任何属性!"啊,但是的,你在以东城[4]。由于对象创建后调用__init__(),因此行self.a = aself.b = bim创建后设置属性ab。您真正想要的是在创建不可变对象之前设置属性ab。一个明显的方法是先创建可变类型(允许在__init__()中设置其属性),然后使不可变类型成为它的子类,并确保实现不可变子类的__new__()方法,以先构造可变版本,然后使其不可变,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Mutable(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b


class ActuallyImmutable(Mutable):
    def __new__(cls, a, b):
        thing = Mutable(a, b)
        thing.__class__ = cls
        return thing

    def __setattr__(self, key, value):
        raise AttributeError("LOL nope srsly.")

现在让我们试着运行它。

1
2
im = ActuallyImmutable(2, 3)
print(im.a, im.b, sep=",")

输出:

1
AttributeError: LOL nope srsly.

"WTF!?这次__setattr__()是什么时候接到电话的?"问题是,ActuallyImmutableMutable的一个子类,在没有明确实现其__init__()的情况下,父类的__init__()ActuallyImmutable对象创建后自动调用,因此总的来说,父类的__init__()被调用两次,一次在im创建之前(这是可以的),一次在__init__()创建之后调用。R(不正常)。所以,让我们再试一次,这次将覆盖AcutallyImmutable.__init__()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Mutable(object):
    def __init__(self, a, b):
        print("Mutable.__init__() called.")
        self.a = a
        self.b = b


class ActuallyImmutable(Mutable):
    def __new__(cls, a, b):
        thing = Mutable(a, b)
        thing.__class__ = cls
        return thing

    # noinspection PyMissingConstructor
    def __init__(self, *args, **kwargs):
        # Do nothing, to prevent it from calling parent's __init__().
        pass

    def __setattr__(self, key, value):
        raise AttributeError("LOL nope srsly.")

现在应该可以了。

1
2
im = ActuallyImmutable(2, 3)
print(im.a, im.b, sep=",")

输出:

1
2, 3

很好,它起作用了。哦,别担心# noinspection PyMissingConstructor,那只是一个Pycharm黑客阻止Pycharm抱怨我没有给父母的__init__()打电话,这显然是我们的目的。最后,为了检查im是否不变,验证im.a = 42是否会给你AttributeError: LOL nope srsly.