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.
号
但为什么呢?为什么我们不能直接覆盖
我是问题操作员,我要回答我自己的问题,因为我想我在打字的中途找到了答案。在别人确认它是正确的之前,我不会把它标为正确的。
这里的这个问题是特别相关的,但是这个问题与这个问题不一样,尽管答案很有启发性(尽管评论变成了关于C和python以及"pythonic"的启发性但深奥的论据),但是应该在这里更清楚地阐述这个问题。我希望这能帮助未来的读者。这个答案中的代码已经在python 3.6.1中得到了验证。
一个不变的对象的问题是,显然,一旦创建了它,就不想设置它的成员。在python中这样做的方法是将
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]。由于对象创建后调用
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!?这次
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 |
。
很好,它起作用了。哦,别担心