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

2023-12-13

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

From Python 文档页面,我找到以下文字。

__new__()主要目的是允许不可变类型(如 int、str 或 tuple)的子类自定义实例创建。也是 通常在自定义元类中重写以自定义类 创建。

But why?为什么我们不能直接覆盖__init__()而不必覆盖__new__()?显然,frozenset,例如,甚至没有实现__init__(); 这是为什么?我的理解是从here在一些罕见的情况下,__new__() and __init__()需要做不同的事情,但据我所知,这只是在酸洗和取消酸洗期间。这是关于什么的特别是不可变类型这需要使用__new__()代替__init__()?


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

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

显然,关于不可变对象的问题是,一旦创建它,​​您就不想设置它的成员。在 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=", ")

Output:

AttributeError: LOL nope.

“但是什么!?”,我听到你问,“我在创建它后没有设置它的任何属性!”啊但是是的,你做到了, 在里面__init__(). Since __init__()叫做after对象被创建,线条self.a = a and self.b = b正在设置属性a and b after的创造im。你真正想要的是设置属性a and b before创建不可变对象。一个明显的方法是创建一个mutable首先输入(您的属性被允许设置在__init__()),然后使不可变的 type a subclass并确保您实施__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=", ")

Output:

AttributeError: LOL nope srsly.

“WTF!?什么时候的?__setattr__()这次接到电话了吗?”问题是,ActuallyImmutable是一个子类Mutable,并且没有明确实施其__init__(), 父类的__init__()被自动调用after的创建ActuallyImmutable对象,所以父母的总数__init__()被调用两次,一次在创建之前im(没关系)并且一次after(这是not OK)。所以让我们再试一次,这次覆盖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=", ")

Output:

2, 3

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • Python Nose 导入错误

    我似乎无法理解鼻子测试框架 https nose readthedocs org en latest 识别文件结构中测试脚本下方的模块 我已经设置了演示该问题的最简单的示例 下面我会解释一下 这是包文件结构 init py foo py t
  • 编辑 scikit-learn 决策树

    我想编辑 sklearn DecisionTree 例如改变条件或切割节点 叶子等 但似乎没有功能可以做到这一点 如果我可以导出到文件 编辑它以导入 如何编辑决策树 环境 Windows 10 python3 3 sklearn 0 17
  • KFold 和 ShuffleSplit CV 有什么区别?

    看起来 KFold 每次迭代对象时都会生成相同的值 而 Shuffle Split 每次都会生成不同的索引 它是否正确 如果是这样 其中一个相对于另一个有什么用处 cv cross validation KFold 10 n folds 2
  • Python 在 chroot 中运行时出现错误

    我尝试在 chroot 中运行一些 Python 程序 但出现以下错误 Could not find platform independent libraries
  • 底图上的子图

    我有一张英国地图和 121 个地点 每个地点有 3 个值 我想绘制 121 个位置中每个位置的三个值的小条形图 目前 这些值绘制为markersize属性 看起来像这样 密集恐惧症情节 https i stack imgur com 5fv
  • 子进程改变目录

    我想在子目录 超级目录中执行脚本 我需要首先进入该子目录 超级目录 我无法得到subprocess进入我的子目录 tducin localhost Projekty tests ve python Python 2 7 4 default
  • 将 API 数据存储到 DataFrame 中

    我正在运行 Python 脚本来从 Interactive Brokers API 收集金融市场数据 连接到API后 终端打印出请求的历史数据 如何将数据保存到数据帧中而不是在终端中流式传输 from ibapi wrapper impor
  • Python sqlite3游标没有属性commit

    当我运行这段代码时 path Scripts wallpapers single png conn sqlite3 connect Users Heaven Library Application Support Dock desktopp
  • 为什么导入 pdb 时出现此错误? “模块”对象没有属性“ascii_letters”

    尝试调试我的代码 我正在导入库pdb import sys from subprocess import check call import pdb functions if name main Code 我收到此错误 File reg p
  • Django 的 URL 覆盖率测试为 0%,为什么?

    使用姜戈鼻子 我对 URL 进行了测试 但 URL 覆盖率仍然为 0 为什么 python manage py 测试配置文件 这是我的报道 Name Stmts Miss Cover Missing profiles 0 0 100 pro
  • 如何使用循环将十进制转换为二进制?

    我想编写一个程序 将十进制数 0 到 9 转换为二进制数 我可以编写如何使用重复除法将十进制数转换为二进制数的代码 但是 我在创建一个以二进制格式打印十进制数字 0 到 9 的循环时遇到了麻烦 这是我的代码 number 0 remaind
  • 为 Networkx 图添加标题?

    我希望我的代码创建一个带有标题的图 使用下面的代码 可以创建绘图 但没有标题 有人可以告诉我我做错了什么吗 import pandas as pd import networkx as nx from networkx algorithms
  • 用Python中的嵌套for循环替换重复的if语句?

    在我编写的下面的代码中 n 4 所以有五个 if 语句 所以如果我想将 n 增加到 比如说 10 那么就会有很多 if 语句 因此我的问题是 如何用更优雅的东西替换所有 if 语句 n p 4 5 number of trials prob
  • 网页抓取 - 前往第 2 页

    如何访问数据集的第二页 无论我做什么 它都只返回第 1 页 import bs4 from urllib request import urlopen as uReq from bs4 import BeautifulSoup as sou
  • Django - 电子邮件发送两次

    每当我使用如下所示的电子邮件设置从views py调用下面的方法时 电子邮件的两份副本都会发送给收件人 并且我收到如下所示的错误 def sendEmailBasic request msg EmailMessage Request Cal
  • smooth_idf 是多余的吗?

    The scikit learn 文档 http scikit learn org stable modules generated sklearn feature extraction text TfidfTransformer html
  • Spyder 如何在同一线程的后台运行 asyncio 事件循环(或者确实如此?)

    我已经研究 asyncio 模块 功能几天了 因为我想将它用于我的应用程序的 IO 绑定部分 并且我认为我现在对它的工作原理有一个合理的理解 或者在至少我认为我已经理解了以下内容 任一时刻 任一线程中只能运行一个异步事件循环 一旦一切都设置
  • scipysolve_ivp() 中的访问时间步长

    我有一个常微分方程系统 正在使用 scipy 的solve ivp 函数求解 它运行良好 但我在访问每个步骤中使用的时间步时遇到问题 我知道solve ivp 将当前时间传递给用户定义的函数 但我需要使用的时间步长 而不是当前时间 为了解决
  • 在Python中从日期时间中减去秒

    我有一个 int 变量 它实际上是秒 让我们调用这个秒数X 我需要得到当前日期和时间 以日期时间格式 减去的结果X秒 Example If X是 65 当前日期是2014 06 03 15 45 00 那么我需要得到结果2014 06 03
  • 从 pandas 数据框中绘制堆积条形图

    我有数据框 payout df head 10 复制以下 Excel 绘图的最简单 最智能和最快的方法是什么 我尝试过不同的方法 但无法让一切都到位 Thanks 如果您只想要一个堆积条形图 那么一种方法是使用循环来绘制数据框中的每一列 并

随机推荐