1
path = [[0, 1, 0, 0], 
       [0, 0, 1, 1], 
       [0, 0, 0, 1], 
       [1, 0, 0, 0]]
print [hex(id(path[0])),hex(id(path[1])), hex(id(path[2]))], id(path[1])-id(path[0]),id(path[2]) -id(path[1])
path[1] = [0,0, 0, 0]
print path
print [hex(id(path[0])),hex(id(path[1])), hex(id(path[2]))], id(path[1])-id(path[0]),id(path[2]) -id(path[1])

这是python 2.7,CPython。结果示例:

['0x107d05638', '0x107cefb90', '0x107cef7e8'] -88744 -936

[[0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0]]

['0x107d05638', '0x107cef8c0', '0x107cef7e8'] -89464 -216

谢谢你。

4

2 回答 2

2

首先,一些重要的注意事项。

  1. id(..)不一定指对象的地址。确实,文档说

    id(object)

    返回对象的“身份”。这是一个整数,保证该对象在其生命周期内是唯一且恒定的。具有不重叠生命周期的两个对象可能具有相同的 id()值。

    CPython 实现细节:这是对象在内存中的地址。

    所以只有在 CPython 中,才能保证是这种情况。

  2. Python中没有二维列表之类的东西。Python 只有非常有限的基本数据结构:集合、字典、整数、字符串、列表……但没有二维列表。二维列表是列表的列表。这可能看起来像一个细节,但你可以例如声明:[[1,0,1],2,'foobar']。所以这只是部分二维列表。Python 只看到一个列表,其中一个元素恰好是一个列表。

    这是什么意思?在您的问题中,列表如下所示:

    +---------------+
    |      list     |
    +---+---+---+---+
    | o | o | o | o |
    +-|-+-|-+-|-+-|-+
      |   |   |   \________________________________________________
      |   |   \________________________________                    \
      |   \______________                      \                    |
      |                  \                      |                   |
    +-v-------------+  +--v------------+  +-----v---------+  +------v--------+
    |      list     |  |      list     |  |      list     |  |      list     |
    +---+---+---+---+  +---+---+---+---+  +---+---+---+---+  +---+---+---+---+
    | o | o | o | o |  | o | o | o | o |  | o | o | o | o |  | o | o | o | o |
    +-|-+---+---+---+  +-|-+-|-+-|-+-|-+  +-|-+-|-+-|-+-|-+  +-|-+-|-+-|-+-|-+
      v   v   v   v      v   v   v   v      v   v   v   v      v   v   v   v
      0   1   0   0      0   0   1   1      0   0   0   1      1   0   0   0
    

    顺便说一下,0s 和1s 也是对象,但它们是对同一对象的引用。

  3. 通常解释器使用堆并在堆上分配。现在堆可以被碎片化,因为早期的对象已经在堆上分配和释放。结果,解释器的目标是找到要分配的对象可以放入的孔。

  4. 列表可以共享子列表。例如:

    a = [1,0]
    b = [[0,0],a,[0,1],[0,0]]
    c = [a]
    

    现在两者b都有c一个引用 object 的元素a。因此,不能同时使两者都b连续c

  5. 如果您执行:

    path[1] = [0,0, 0, 0]
    

    您基本上首先构建一个新列表[0,0,0,0]。由于旧列表path[1]仍在内存中,因此无法填补该漏洞。所以 Python 解释器必须找到另一个地方来定位它[0,0,0,0]。然后它设置path[1]对该列表的引用,最后(通常在更新引用计数之后),它将删除旧列表path[1](这也可以延迟到应该释放内存),从而标记曾经被占用的地方作为空置。

这些笔记基本上回答了这个问题:没有二维列表:所有对象都在堆上分配,因为发生了很多分配/释放(例如,即使在启动和加载库时)。对象将被分配到哪里是不确定的。由于可以共享对象,因此所有列表的内存布局不能同时是连续的。

最后请注意,Python 通常假定程序员的便利性效率更重要。Python 的效率相当低:它是动态类型的,(例如通过使用 MRO)解决动态绑定的方法效率低下,通常有很多回退机制,这也需要计算工作量。在中,他们还引入了任意长度的整数等。这些特性都给 CPU 带来了相当大的负担,但 Python 的理念通常是程序员的便利性高于 CPU 效率,因为支付额外的程序员费用通常会花费更多而不是购买两台新服务器。

于 2017-03-14T22:10:11.057 回答
0

Python 对象的所有内存都在私有堆中进行管理。2D 列表是一种数据结构和 Python 对象的示例(因为从基本类型到类类型的所有内容在 Python 中都被视为“对象”)。

管理私有堆的 Python 内存管理器根据与 2D 数组类型相关的不同内存管理策略来决定如何为 2D 数组对象分配内存。显然,在这种情况下,确保为整个数组连续分配内存是不利的(可能出于性能或开销原因)。

这说明了在高级语言中不需要指针数学运算的想法。在 C 中,A[x][y] 的简单公式用于检索存储的值,但在 Python 中,我们可以变得更复杂。

于 2017-03-14T21:48:43.437 回答