为什么新样式类和旧样式类在这种情况下有不同的行为?

2023-11-23

我发现了一些有趣的东西,这是一段代码:

class A(object):
    def __init__(self):
        print "A init"

    def __del__(self):
        print "A del"

class B(object):
    a = A()

如果我运行这段代码,我将得到:

A init

但如果我改变class B(object) to class B(), 我会得到:

A init
A del

我在__del__ 文档:

不保证del() 方法是为对象调用的 当解释器退出时仍然存在。

那么,我想这是因为B.a仍然被引用(由类引用B) 当解释器存在时。

所以,我添加了一个del B在解释器手动存在之前,然后我发现a.__del__()被称为。

现在,我对此有点困惑。为什么是a.__del__()使用旧样式类时调用?为什么新旧样式类有不同的行为?

我发现了类似的问题here,但我认为答案还不够明确。


TL;DR:这是一个老问题在 CPython 中,这个问题最终被修复了CPython 3.4。在 3.4 之前的 CPython 版本中,由模块全局变量引用的引用循环保持活动状态的对象在解释器退出时未正确完成。新式类在其内部具有隐式循环type实例;旧式类(类型classobj)没有隐式引用循环。

尽管在这种情况下已修复,CPy​​thon 3.4 文档仍然建议不要依赖__del__口译员退出时被叫到 - 认为自己受到警告。


新样式类本身具有引用循环:最值得注意的是

>>> class A(object):
...     pass
>>> A.__mro__[0] is A
True

这意味着它们不能立即删除*,只能在垃圾收集器运行时删除。由于对它们的引用由主模块保存,因此它们将保留在内存中直到解释器关闭。最后,在模块清理期间,main 中的所有模块全局名称都被设置为指向None,并且引用计数减少到零的任何对象(例如您的旧式类)也会被删除。然而,具有引用循环的新型类不会因此被发布/最终确定。

循环垃圾收集器不会在解释器出口处运行(这是由CPython 文档:

不保证__del__()为解释器退出时仍然存在的对象调用方法。


现在,Python 2 中的旧式类没有隐式循环。当 CPython 模块清理/关闭代码将全局变量设置为None,对类的唯一剩余引用B被丢弃;然后B已删除,最后引用a被丢弃,并且a也已敲定。


为了证明新式类有循环并且需要 GC 扫描,而旧式类则不需要,您可以在 CPython 2 中尝试以下程序(CPython 3 不再有旧式类):

import gc
class A(object):
    def __init__(self):
        print("A init")

    def __del__(self):
        print("A del")

class B(object):
    a = A()

del B
print("About to execute gc.collect()")
gc.collect()

With B作为上面的新式类,输出是

A init
About to execute gc.collect()
A del

With B作为旧式类(class B:),输出为

A init
A del
About to execute gc.collect()

也就是说,new-style类在之后才被删除gc.collect()尽管最后一次外部引用它已经被删除了;但旧式类立即被删除。


其中大部分已经fixed in Python 3.4: 谢谢PEP 442,其中包括基于GC代码的模块关闭程序。现在,即使在解释器退出时,模块全局变量也会使用普通垃圾收集来完成。如果你在Python 3.4下运行你的程序,程序将打印

A init
A del

而对于 Python

A init

(Do note其他实现仍然可能会或可能不会执行__del__此时,无论它们的版本高于、等于或低于 3.4)

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

为什么新样式类和旧样式类在这种情况下有不同的行为? 的相关文章

随机推荐

  • MySQL和MongoDB之间的数据同步

    我正在寻找一种将数据从 MySQL 同步到 MongoDB 的方法 我不想只是复制我的数据 我希望它们永久同步 有谁知道钨复制器是否可以完成这项工作 我可以看到它复制数据 但是当 MySQL 数据库中发生更改时自动复制数据怎么样 如果不可能
  • 在 Objective-C 中如何测试字符串是否为空?

    我如何测试是否NSStringObjective C 中是空的吗 您可以检查是否 string length 0 这将检查它是否是一个有效但空的字符串 以及它是否为零 因为调用lengthon nil 也会返回 0
  • Office Open XML 中的 之间有什么区别?

    这两个 Office Open XML 片段有什么区别
  • android Camera2 API + TextureView 覆盖用于在相机预览上绘图

    因此 我需要覆盖camera2预览并通过在顶部分层透明覆盖层在预览视频图像上绘制一个矩形 我从这里开始使用基本的 Camera2 代码 https github com googlesamples android Camera2Basic
  • 如何在 DT::datatable 中使用服务器端处理?

    我正在使用 DT datatable 来可视化 R markdown 文件中的表 R markdown file library DT r viewdata this is an example but my actual dataset
  • 如何将 hashMap 与 JTable 一起使用

    我有一个 hashMap 我希望在 JTable 中查看它的数据 但我在获取 hashMap 的列和行数量以及要显示的数据时遇到了麻烦 我有一个 hashmap 它以 accountID 作为键和一个学生的对象 其中每个学生都有自己的数据
  • SwiftUI 在圆角矩形内屏蔽一个矩形

    你好呀 我想知道 在 SwiftUI 中 如何屏蔽圆角矩形的内容 以便子矩形剪掉角 在我的示例中 我在 zstack 上有一个白色圆角矩形和一个粉色矩形 我尝试应用剪切 但粉色矩形不符合边角 我尝试将 mask 应用于白色矩形 但它给出了与
  • 无法在 pthread_create 函数中将 '*void(MyClass::*)(void*) 转换为 void*(*)(void*)

    我正在尝试使用 CameraManager 类创建一个新线程 但出现以下错误 无法在 pthread create 函数中将 void CameraManager void 转换为 void void 我在cameramanager h文件
  • 如何将继承的对象字符串化为 JSON?

    使用 JSON stringify 时 json2 js 似乎忽略了父对象的成员 例子 require json2 js function WorldObject type this position 4 function Actor va
  • 可以使用单例 DAO 对象吗?

    考虑以下类的结构 BaseDAO使用方法来创建PreparedStatement并从池中获取连接 AccountDAO extends BaseDAO跟 共事Account通过 JDBC 的表 这个类是单例类 AccountService女
  • 我如何知道 jQuery 是否有待处理的 Ajax 请求?

    我在使用我们制作的 jQuery 控件时遇到一些问题 假设您有一个下拉列表 允许您输入要查找的项目的 ID 当您按 ENTER 或在文本框中失去焦点时 它会通过 jQuery 验证您输入的 ID 是否正确 如果不正确则显示警报 t 问题是
  • 无法通过 PDO_ODBC 从 Access 检索 UTF-8 重音字符

    我正在尝试将 Access DB 转换为 MySQL 一切都很完美 除了一把大活动扳手之外 如果访问数据库有任何非标准字符 它将无法工作 我的查询会告诉我 Incorrect string value xE9d 如果我直接回显具有 无效 字
  • 创建名称助手,将名字和姓氏分开

    我正在寻找一些有关如何获取属性并通过方法处理它以返回不同内容的帮助 但我以前从未这样做过 而且不知道从哪里开始 我想尝试将 name string 属性从 George Washington 或 John Quincy Adams 更改为仅
  • ASP.NET 5 EntityFramework.Core 7.0.0-rc1-final 问题 - 编译器希望引用 7.0.0.0,但未找到

    我有同样的问题 我在我的中添加了以下依赖项project json file dependencies EntityFramework 7 0 0 beta4 EntityFramework Core 7 0 0 rc1 final Ent
  • Android 获取缓存大小的方法

    我在我的测试应用程序中使用 fedor 的延迟加载列表实现 只需单击一个按钮即可清除缓存 如何获取列表视图中加载图像的缓存大小并以编程方式清除缓存 以下是保存缓存图像的代码 public ImageLoader Context contex
  • 理解记忆障碍

    我正在尝试在对 java 无锁程序员有用的级别上理解内存屏障 我认为 这个级别介于学习易失性和从 x86 手册中学习存储 加载缓冲区的工作之间 我花了一些时间阅读了很多博客 食谱 并得出了下面的摘要 有知识渊博的人可以看一下摘要 看看我是否
  • 如何将相对布局设置为默认布局

    我通常在大部分活动中使用相对布局 我想让 Android studio 在创建新活动时默认创建相对布局 而不是约束布局 有没有办法做到这一点 右键单击布局文件夹 gt 新建 gt 编辑文件模板 打开一个对话框 转到 其他 选项卡 更改内容
  • C++11 从频繁变化的范围生成随机数

    问 如何从先验未知范围生成 许多 均匀分布的整数 就性能而言 生成数百万个数字 首选方式是什么 上下文 在我的应用程序中 我必须在许多地方生成许多伪随机数 我对生成器使用单例模式来保持应用程序运行的可重复性 在我的例子中 分布总是均匀的 但
  • 使用 jquery 动态创建元素

    我正在尝试使用 jquery 创建元素 当我单击链接时 我想创建一个元素 p 给它一些文本 然后将其放入我的一个 div 中 另外 我想检查点击了哪个链接 这样我就可以将创建的 p 放在右侧的 div 中 关于我做错的地方有什么解决方案吗
  • 为什么新样式类和旧样式类在这种情况下有不同的行为?

    我发现了一些有趣的东西 这是一段代码 class A object def init self print A init def del self print A del class B object a A 如果我运行这段代码 我将得到