为什么Python不可变类型(例如int,str或tuple)需要使用__new __()而不是__init __()? [英] Why do Python immutable types (like int, str, or tuple) need to use `__new__()` instead of just `__init__()`?

查看:295
本文介绍了为什么Python不可变类型(例如int,str或tuple)需要使用__new __()而不是__init __()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题与this 。这些链接在这里无法回答我的问题。 虽然,几乎可以回答我的问题,但不能,因为答案中的代码无法在Python 3.6中运行,而且无论如何该问题都不是我在这里问的具体问题。 (请参阅下面的我自己的答案。

This question is related to, but not a duplicate of, this, this, this, and this. Those links don't answer my question here. This though, almost answers my questions but doesn't, because the code in the answer doesn't run in Python 3.6 and in any case the question there isn't specifically about what I'm asking here. (See my own answer below.

来自 Python文档页面,我发现以下内容。

From the Python documentation page, I find the following text.


__new __()主要用于允许不可变类型的子类(例如int,str或tuple)自定义实例创建,也通常在自定义元类中覆盖
以便自定义类
创建。

__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 __()是必需的o做不同的事情,但据我所知,这仅在酸洗和酸洗时进行。 特别是不可变类型有什么用,它需要使用 __ new __()而不是 __ init __()

But why? Why can't we just override __init__() instead of having to override __new__()? Apparently, frozenset, for example, doesn't even implement __init__(); why is that? I understand from here that in some rare cases, __new__() and __init__() are required to do different things, but as far as I can see that's only during pickling and unpickling. What is it about immutable types in particular that requires the use of __new__() instead of __init__()?

推荐答案

我是问题OP,我将回答我自己的问题,因为我认为我在输入答案的途中找到答案。在其他人确认它正确之前,我不会将其标记为正确。

I'm the question OP and I'm going to answer my own question because I think I found out the answer half-way through typing it. I'm not going to mark it as correct until others have confirmed it to be correct.

此处的问题特别相关,但该问题与该问题不同,尽管答案很有启发性(尽管注释变成了关于C和Python以及 pythonic的启发性但深奥的论点),在这里应该更清楚地阐述它,以专门解决这个问题。希望这对以后的读者有所帮助。此答案中的代码已在Python 3.6.1中进行了验证。

This question here is particularly relevant, but the question wasn't the same as this question, and although the answer was very enlightening (though the comments turned into enlightening but esoteric arguments about C and Python and "pythonic"), it should be set out more clearly here to specifically address this question. I hope this will help future readers. The code in this answer has been verified in Python 3.6.1.

关于不可变对象的事情是,您不希望在对象被设置后立即对其进行设置。创建,显然。在Python中执行此操作的方法是重写 __ setattr __()特殊方法以 raise 一个错误( AttributeError ),这样人们就无法执行 my_immutable_object.x = 3 之类的操作。以下面的自定义不可变类为例。

The thing about an immutable object, is that you don't want to set its members once it's been created, obviously. The way you do that in Python is to override the __setattr__() special method to raise an error (AttributeError), so that people can't do things like my_immutable_object.x = 3. Take the following custom immutable class for example.

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 设置属性 a b 之后创建 im 。您真正想要的是将属性 a b 设置为不可变对象,创建。一种明显的方法是首先创建一个 mutable 类型(允许您在 __ init __()的属性 c>),然后将 immutable 键入其 subclass ,并确保实现 __ new __()不可变子类的方法首先构造一个可变版本,然后使其变为不可变,如下所示。

"But what!?", I hear you ask, "I didn't set any of its attributes after it's been created!" Ah but yes you did, in the __init__(). Since __init__() is called after the object is created, the lines self.a = a and self.b = b are setting the attributes a and b after the creation of im. What you really want is to set the attributes a and b before the immutable object is created. An obvious way to do that is to create a mutable type first (whose attributes you are allowed to set in __init__()), and then make the immutable type a subclass of it, and make sure you implement the __new__() method of the immutable child class to construct a mutable version first, and then make it immutable, like the following.

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

现在让我们尝试运行它。

Now let's try running it.

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

输出:

AttributeError: LOL nope srsly.

WTF !? __ setattr __()这次打电话吗?事实是, ActuallyImmutable Mutable 的子类,并且没有显式实现其 __ init __( ),父类的 __ init __()在创建 ActuallyImmutable之后自动被称为对象,因此,在创建 im __ init __()被调用两次。 $ c>(确定),然后在之后不正确)。因此,让我们再试一次,这次覆盖了 AcutallyImmutable .__ init __()

"WTF!? When did __setattr__() get called this time?" The thing is, ActuallyImmutable is a subclass of Mutable, and without explicitly implementing its __init__(), the parent class's __init__() is automatically called after the creation of the ActuallyImmutable object, so in total the parent's __init__() is called twice, once before the creation of im (which is OK) and once after (which is not OK). So let's try again, this time overriding 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骇客,可以阻止PyCharm抱怨我没有打电话给父母的 __ init__。 (),这显然是我们在此打算的。最后,只是要检查 im 确实是不可变的,请验证 im.a = 42 会给您 AttributeError:大声笑不行。

Good, it worked. Oh, don't worry about the # noinspection PyMissingConstructor, that's just a PyCharm hack to stop PyCharm from complaining that I didn't call the parent's __init__(), which obviously is what we intend here. And finally just to check that im really is immutable, verify that im.a = 42 will give you AttributeError: LOL nope srsly..

这篇关于为什么Python不可变类型(例如int,str或tuple)需要使用__new __()而不是__init __()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆