python - 为什么 Python 不可变类型(如 int、str 或 tuple)需要使用 `__new__()` 而不仅仅是 `__init__()` ?

标签 python python-3.x constructor subclass

此问题与 this 相关,但不重复。 , this , this ,和 this 。这些链接并不能回答我的问题。 This though ,几乎回答了我的问题,但没有回答,因为答案中的代码不能在 Python 3.6 中运行,并且无论如何,这里的问题并不是专门关于我在这里问的内容。 (请参阅下面我自己的回答。

来自the Python documentation page ,我找到以下文字。

__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__()这是为什么?我从here了解到在极少数情况下,需要 __new__()__init__() 执行不同的操作,但据我所知,这仅在酸洗和取消酸洗期间。 不可变类型需要使用__new__()而不是__init__(),具体是什么?

最佳答案

我是问题OP,我要回答我自己的问题,因为我想我在打字的过程中找到了答案。在其他人确认它是正确的之前,我不会将其标记为正确。

This question here特别相关,但问题与这个问题不同,尽管答案非常有启发性(尽管评论变成了关于C和Python和“pythonic”的启发性但深奥的论点),但应该更清楚地阐述这里专门解答一下这个问题。我希望这对 future 的读者有所帮助。本答案中的代码已在 Python 3.6.1 中验证。

关于不可变对象(immutable对象)的问题是,显然,一旦创建它,​​您就不想设置它的成员。在 Python 中执行此操作的方法是覆盖 __setattr__()特殊方法raise错误( AttributeError ),这样人们就不能做类似 my_immutable_object.x = 3 的事情。以下面的自定义不可变类为例。

class Immutable(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

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

让我们尝试使用它。

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

输出:

AttributeError: LOL nope.

“但是什么!?”,我听到你问,“在创建它之后我没有设置它的任何属性!”啊但是是的,你做到了,在 __init__() 。自 __init__()在创建对象之后调用 self.a = a 行和self.b = b正在设置属性 ab创建 im 之后 。你真正想要的是设置属性 ab 创建不可变对象(immutable对象)之前。一个明显的方法是首先创建一个可变类型(您可以__init__() 中设置其属性),然后使不可变 输入它的子类,并确保您实现 __new__()不可变子类的方法首先构造一个可变版本,然后使其不可变,如下所示。

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.")

现在让我们尝试运行它。

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

输出:

AttributeError: LOL nope srsly.

“WTF!?这次__setattr__()什么时候被叫到的?”问题是,ActuallyImmutableMutable 的子类,并且没有明确实现其 __init__() ,父类的__init__()创建ActuallyImmutable自动调用对象,因此父级的总数为 __init__()被调用两次,一次是在创建 im 之前(这是可以的)和一次之后(这是不行)。所以让我们再试一次,这次覆盖 AcutallyImmutable.__init__() .

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.")

现在应该可以工作了。

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

输出:

2, 3

很好,成功了。哦,不用担心# noinspection PyMissingConstructor ,这只是 PyCharm 的 hack,目的是阻止 PyCharm 提示我没有给 parent 打电话 __init__() ,这显然是我们的意图。最后只是为了检查 im确实是不可变的,请验证 im.a = 42会给你AttributeError: LOL nope srsly. .

关于python - 为什么 Python 不可变类型(如 int、str 或 tuple)需要使用 `__new__()` 而不仅仅是 `__init__()` ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56314592/

相关文章:

python - 我在哪里可以找到解决方案的良好练习python问题?

Python 3 列表理解求和与串联

python-3.x - 无法从相机OpenCV读取帧

python - 定义在 Python 中给出给定范围内的随机值的函数

c# - 在同一类 c# 中使用构造函数调用构造函数并在同一构造函数中调用基础构造函数

python - 无法解决 ValueError : The create_choropleth figure factory requires the plotly-geo package

Python - 如何正确模拟类方法中的 cls 参数以引用静态变量

python - 异或十六进制/ASCII/转换问题

PHP空构造函数

具有范围限制的 Java 类构造函数参数