我正在python shell中尝试这些,并且得到了相当混乱的结果。
>>> p = [1, 2, 3, 4, 5, 6, 7, 8]
>>> p
[1, 2, 3, 4, 5, 6, 7, 8]
>>> p[2:8:2]
[3, 5, 7]
>>> id(p[2:8:2])
37798416
>>> id(p[2:8:2])
37798416
>>> id(p[2:8:2])
50868392
请注意ID是如何第三次更改的!
>>> id(p[2:8:2])
37798336
再次更改! 问题1:这是怎么发生的以及为什么发生的?
问题2:
>>> p[2:8:2] = [33,55,77]
>>> p
[1, 2, 33, 4, 55, 6, 77, 8]
python如何精确“存储” p [2:8:2]? (可能“存储”不是正确的词,但希望您能理解)。它看起来好像不是与原始列表不同的列表(尽管它由原始列表中的非顺序不可变项组成),因为对 this 列表的更改反映在原始列表中!
答案 0 :(得分:4)
切片(很少有例外)会为要切片的内容制作全新的副本。因此,所有PySliceObject
检查都告诉您,有时新的x
重用上一次的内存,有时又使用不同的内存。确切的行为是纯实现细节。在CPython(参考解释器)中,id
恰好对应于内存地址,因此您所看到的只是分配器的行为工件,而不是切片的深层含义。
关于问题2:在分配上下文中使用时,切片会修改原始序列,根本不会创建新的list
。不要试图在切片(面向读取,创建新序列)和切片分配(面向写入,修改现有序列)之间绘制有意义的相似之处;引擎盖下的行为几乎在各个方面都不同。
答案 1 :(得分:1)
问题1:
保证对象的ID既唯一,又在该对象的生存期内保持不变。请参见here in the Python library docs:
id(object)
-返回对象的身份。这是一个整数,可以保证在此对象的生存期内唯一且恒定。具有不重叠生存期的两个对象可能具有相同的id()
值。
由于您正在使用切片创建和销毁对象,因此id实际上遵循规则。
如果您使用的是CPython的引用(并且我怀疑是最常见的)实现,则它只是为您提供对象的内存地址。可以在Python/bltinmodule.c
中找到源代码,下面对其进行了简化和注释:
static PyObject *builtin_id(PyModuleDef *self, PyObject *v) {
PyObject *id = PyLong_FromVoidPtr(v); // Turn object address into
return id; // long and return it.
}
这确保了它的唯一性,并且内存分配调用的变化和顺序也解释了为什么它可以重复和/或不同。
问题2:
分配给“切片”实际上不是涉及创建切片对象并对其进行分配。只需将切片符号指定的现有值中的某些值设置为赋值右侧的值即可。
更多细节可以在CPython源代码的sliceobject
文件中找到,尤其是Objects/sliceobject.c和Include/sliceobject.h。这些操作涉及创建一个PySliceObject
元组的{start, stop, step}
。
当将此元组应用于分配右侧的对象时,例如x = y[2:8:2]
,它会使用PySliceObject
根据{{ 1}},仅获取 相关元素。
在左侧使用时,例如x
,它使用y
来决定x[2:8:2] = [33,55,77]
的哪些元素被设置为正确的。