gpt4 book ai didi

python - Python内存管理见解— id()

转载 作者:太空狗 更新时间:2023-10-30 00:30:31 26 4
gpt4 key购买 nike

id()。从查看非相同对象中相同属性的地址开始。但我想现在没关系了。到代码:

class T(object):
pass

class N(object):
pass

第一次测试(在交互控制台中):
n = N()
t = T()
id(n)
# prints 4298619728
id(t)
# prints 4298619792

事实上,这并不奇怪。 n.__class__不同于 t.__class__,所以很明显,它们不可能是同一个对象。在这个时候,这些对象之间的唯一区别是 __class__吗?假设没有,作为:
>>> n1 = N()
>>> n2 = N()
>>> id(n1) == id(n2)
False

或者Python简单地创建单独的对象,即使它们是完全相同的,内容是明智的,而不是分配名称 n1n2,首先,是相同的对象(在内存中),当修改了 n1还是 n2时重新分配?为什么呢?我理解这可能是一个常规、优化、情绪、低级问题(不要饶恕我)的问题,但我仍然好奇。
现在,与前面相同的类, T()N()在shell中一个接一个执行:
>>> id(N())
4298619728
>>> id(N())
4298619792
>>> id(N())
4298619728
>>> id(N())
4298619792

为什么要玩杂耍?
但是奇怪的部分来了。同样的,贝壳:
>>> id(N()), id(T())
(4298619728, 4298619728)
>>> id(N()), id(T())
(4298619728, 4298619728)
>>> id(N()), id(T())
(4298619728, 4298619728)

不仅杂耍停止,而且n-()和t-()看起来是同一个对象。因为它们不可能是,所以我理解这是因为在整个语句结束之前, N()调用后返回的whatever id()将被销毁。
我意识到这可能是一个很难回答的问题。但我希望有人能告诉我我在这里观察什么,我的理解是否正确,分享一些关于翻译的内部运作和记忆管理的黑暗魔法,或者也许指向一些关于这个主题的好资源?
谢谢你在这件事上的时间。

最佳答案

你问了很多问题。我会尽我所能回答其中的一些问题,希望你能解决剩下的问题(如果你需要帮助的话)。
第一个问题:解释id的行为

>>> n1 = N()
>>> n2 = N()
>>> id(n1) == id(n2)
False

这表明Python每次调用对象构造函数时都会创建一个新对象。这是有道理的,因为这正是你所要求的!如果您只想分配一个对象,但要给它两个名称,那么您可以编写:
>>> n1 = N()
>>> n2 = n1
>>> id(n1) == id(n2)
True

第二个问题:为什么不写下来?
接下来您会问,为什么python没有为对象分配实现一个写时拷贝策略。那么,每次调用构造函数时构造对象的当前策略是:
易于实施;
明确的(做你要求的事情);
易于记录和理解。
此外,copy-on-write的用例也不具有说服力。只有在创建了许多相同的对象且从未修改过这些对象时,它才保存存储空间。但在这种情况下,为什么要创建许多相同的对象?为什么不使用一个对象?
第三个问题:解释分配行为
在cpython中,对象的 id是(秘密的!)它的地址在内存中。参见 builtin_id, line 907中的函数 bltinmodule.c
你可以通过用 __init____del__方法制作一个类来研究Python的内存分配行为:
class N:
def __init__(self):
print "Creating", id(self)
def __del__(self):
print "Destroying", id(self)

>>> id(N())
Creating 4300023352
Destroying 4300023352
4300023352

您可以看到python能够立即销毁该对象,这允许它回收空间以供下次分配时重用。python使用 reference counting跟踪每个对象有多少个引用,当不再有对象引用时,它将被销毁。在同一语句的执行中,同一内存可以重复使用多次。例如:
>>> id(N()), id(N()), id(N())
Creating 4300023352
Destroying 4300023352
Creating 4300023352
Destroying 4300023352
Creating 4300023352
Destroying 4300023352
(4300023352, 4300023352, 4300023352)

第四个问题:解释“杂耍”
恐怕我无法重现您所展示的“杂耍”行为(即交替创建的对象得到不同的地址)。你能提供更多的细节,比如python版本和操作系统吗?如果你使用我的课,你会得到什么结果?
好吧,如果我让我的类从 N继承,我就可以复制这个杂耍。
我有一个关于为什么会发生这种情况的理论,但我没有在调试器中检查过,所以请稍加考虑。
首先,您需要了解Python的内存管理器是如何工作的。通读 N完成后再回来。我会等的。

都明白了吗?很好。所以现在您知道python通过按大小将小对象排序到池中来管理它们:每个4 kib池包含小范围的对象,并且有一个空闲列表帮助分配器快速找到下一个要分配的对象的插槽。
现在,python交互shell也在创建对象:例如抽象语法树和编译的字节码。我的理论是,当 object是一个新类型的类时,它的大小是与交互shell分配的其他对象相同的池。所以事件的顺序如下:
用户输入 obmalloc.c
python在pool p中为刚创建的对象分配一个槽(称这个槽为a)。
python销毁对象并将其插槽返回到pool p的空闲列表。
交互式shell分配了一些对象,称之为o。这个大小正好适合放入池p,所以它得到了刚刚释放的插槽a。
用户再次输入 N
python在pool p中为刚刚创建的对象分配一个槽。插槽A已满(仍包含对象O),因此它改为获取插槽B。
交互式shell忘记了对象o,因此它被销毁,插槽a被返回到池p的空闲列表。
你可以看到这解释了交替行为。在用户类型 id(N())的情况下,交互shell没有机会将其划桨在两个分配之间,因此它们都可以进入池中的同一个槽中。
这也解释了为什么旧样式对象不会发生这种情况。据推测,旧样式的对象大小不同,因此它们进入不同的池,并且不会与交互shell创建的任何对象共享插槽。
第五个问题:交互式shell可能会分配哪些对象?
有关详细信息,请参见 id(N()),但基本上是交互式shell:
读取输入并分配包含代码的字符串。
调用解析器,解析器构造描述代码的抽象语法树。
调用编译器,编译器构造编译的字节码。
调用评估器,它为堆栈帧、局部变量、全局变量分配对象。
我不知道这些东西中的哪一个是“杂耍”的罪魁祸首。不是输入字符串(字符串有自己的专用分配器);而不是抽象语法树(在编译后会被扔掉)。可能是字节码对象。

关于python - Python内存管理见解— id(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6733477/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com