什么时候创建python对象?

时间:2017-06-13 18:45:56

标签: python object garbage-collection cpython python-internals

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

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

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

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

>> id(27)
28487498

我从来没有创建过具有值27的对象,即我从未写过b=27仍然以某种方式我得到了一个唯一的标识符。这是否意味着对象是在内存中创建的?如果是,即使这样,该对象应该有0个引用,它应该被垃圾收集。

那么,什么时候实际在Memory中创建了一个Object?

如果我错了,请告诉我。

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

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

28487496

在这种情况下,Python会记住对23号本身的引用,Python如何做到这一点?

1 个答案:

答案 0 :(得分:4)

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

首先,当你写

b = 27

两件事事情发生了。计算27表达式,导致整数对象被压入堆栈,然后,作为单独的步骤,将对象分配给b分配不会创建对象

如果你这样做了:

27

仍在评估27表达式。该对象将被创建 * ,然后再次销毁,因为引用计数再次回落为0。

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

id(27)

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

我将使用可变对象而不是整数来说明创建了一个新对象;所以不是id(27)我会使用id([])并要求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),因为不可变对象就像整数和元组一样,实际上是用编译的字节码缓存的;这些是在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)在堆上创建一个整数对象。
  • 许多内置函数会根据需要创建新对象,并从调用中返回
  • classdef语句和lambda表达式创建对象(类对象,函数和更多函数,这些都是对象)。

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

由于他们被实习,20 + 3的结果会返回该单一副本,而id()仍然会与您直接询问id(23)时的结果相同。

还有更多实施细节;还有更多。一些字符串对象被实习(参见my answer here)。在交互式解释器中评估的代码一次编译一个顶级块,但在脚本编译中,每个范围都会编译。因为常量附加到已编译的代码对象,这意味着共享常量时存在差异。等等。

datamodel documentation中明确记录了唯一不能一直重新创建的对象,因为它们是单身人士; None是其中最突出的。