gpt4 book ai didi

python - 什么时候创建python对象?

转载 作者:行者123 更新时间:2023-11-28 22:26:46 25 4
gpt4 key购买 nike

Python 的 id() 函数返回对象的唯一标识符。所以当在我的终端我做类似的事情:

>> a = 23
>> id(a)
28487496

现在,我知道 python 会跟踪所有创建的对象和对该对象的引用数,当值达到 0 时,该对象将被垃圾回收。

我想知道的是当我这样做时会发生什么:

>> id(27)
28487498

我从来没有创建一个值为 27 的对象,即我从来没有写过 b=27 仍然不知何故我得到了一个唯一的标识符。这是否意味着在内存中创建了一个对象?如果是,那么即使这样也应该有 0 个对该对象的引用,并且它应该已被垃圾收集。

那么,什么时候在内存中实际创建对象?

如果我哪里错了,请告诉我。

我刚刚发现的另一个有趣的事情是:

>> a = 23
>> id(a)
28487496
>> id(20 + 3)

28487496

在这种情况下,Python 自己记住了对数字 23 的引用,Python 是如何做到这一点的?

最佳答案

对象是根据需要在不同的地方创建的。

开始,当你写的时候

b = 27

事情发生了。 27 表达式被求值,导致一个整数对象被压入堆栈,然后,作为一个单独的步骤,该对象被分配给 b赋值不创建对象

如果你这样做:

27

27 表达式仍然被计算。该对象将被创建*,然后随着引用计数再次降回 0 再次销毁。

这是必需的,因为您可以将该对象传递给另一个函数:

id(27)

需要一些东西 传递给id() 函数。因此 27 被添加到堆栈中,以便您可以调用该函数。

我将使用可变对象而不是整数来说明创建了一个新对象;所以我将使用 id([]) 而不是 id(27) 并询问 dis module向我展示 Python 将执行的字节码:

>>> import dis
>>> dis.dis(compile('id([])', '', 'exec'))
1 0 LOAD_NAME 0 (id)
2 BUILD_LIST 0
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE

BUILD_LIST 0 opcode用于创建空列表对象并将其压入堆栈,CALL_FUNCTION 1然后调用id从栈中传入一个值,就是那个列表。

我没有使用id(27),因为不可变对象(immutable对象),例如整数和元组等实际上是与编译的字节码一起缓存的;这些是在 Python 编译代码时创建的(或者当您从磁盘加载 .pyc 字节码缓存时):

>>> dis.dis(compile('id(27)', '', 'exec'))
1 0 LOAD_NAME 0 (id)
2 LOAD_CONST 0 (27)
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 1 (None)
10 RETURN_VALUE

注意 LOAD_CONST ,它从 co_consts 结构加载数据:

>>> compile('id(27)', '', 'exec').co_consts
(27, None)

因此可以在编译时创建对象,或者在针对特定 Python 语法执行特殊操作码时创建对象。

还有更多的地方:

  • 还有更多操作码,例如用于创建列表、元组、字典、集合和字符串。
  • 当您创建一个类的实例时,type.__new__ 将在堆上创建一个实例对象。因此 CustomClass(arg1, arg2) 创建了一个具有正确类型的对象。
  • 这同样适用于所有内置类型; int(somevalue) 在堆上创建一个整数对象。
  • 大量内置函数将根据需要创建新对象,并从调用中返回这些对象
  • class, def 语句和lambda 表达式创建对象(类对象,函数,更多的函数,这些也是对象) .

* 小整数实际上是 intern 的;出于性能原因,CPython 为 -5 到 256 之间的每个整数保留一份副本,因此这些对象实际上只创建一次,并在需要的任何地方引用。参见 "is" operator behaves unexpectedly with integers .出于这个答案的目的,我忽略了这一点。

因为它们是 interned 的,20 + 3 的结果返回了那个单一的副本并且 id() 仍然和你请求 id(23) 直接。

有更多的实现细节;还有更多。一些字符串对象被保留(参见 my answer here )。在交互式解释器中评估的代码一次编译一个顶级 block ,但在脚本中编译是按作用域完成的。因为常量附加到编译后的代码对象,这意味着在何时共享常量方面存在差异。等等等等

您唯一可以依赖的不会一直被重新创建的对象明确记录在 datamodel documentation 中。作为单例; None 是其中最突出的。

关于python - 什么时候创建python对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44529269/

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