我知道 Python 字符串是不可变的,这意味着
letters = "world"
letters += "sth"
连接后会给我一个不同的字符串对象begin: id(letters): 1828275686960
end: id(letters): 1828278265776
但是,当我运行 for 循环附加到字符串时,结果是字符串对象在 for 循环期间保持不变:letters = "helloworld"
print("before for-loop:")
print(id(letters))
print("in for-loop")
for i in range(5):
letters += str(i)
print(id(letters))
输出:before for-loop:
2101555236144
in for-loop
2101557044464
2101557044464
2101557044464
2101557044464
2101557044464
显然是 letter
的底层字符串对象指向在 for 循环期间没有改变,这与字符串应该是不可变的概念相矛盾。这是 Python 在幕后执行的某种优化吗?
最佳答案
来自 documentation :
id(object)
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
CPython implementation detail: This is the address of the object in memory.
方法
id()
在这种情况下,存储的字符串的内存地址为 source code向我们展示:static PyObject *
builtin_id(PyModuleDef *self, PyObject *v)
/*[clinic end generated code: output=0aa640785f697f65 input=5a534136419631f4]*/
{
PyObject *id = PyLong_FromVoidPtr(v);
if (id && PySys_Audit("builtins.id", "O", id) < 0) {
Py_DECREF(id);
return NULL;
}
return id;
}
发生的事情是两个对象的生命结束和开始确实重叠。Python 保证字符串的不变性,只要它们还活着。
如 article @kris 建议显示:
import _ctypes
a = "abcd"
a += "e"
before_f_id = id(a)
a += "f"
print(a)
print( _ctypes.PyObj_FromPtr(before_f_id) ) # prints: "abcdef"
字符串 a
结束是生命并且不保证可以恢复给定内存位置,实际上上面的例子表明它是重复使用 对于新变量。我们可以看看它是如何实现的under the hood在
unicode_concatenate
查看最后几行代码的方法:res = v;
PyUnicode_Append(&res, w);
return res;
哪里v
和 w
是表达式中的那些:v += w
方法PyUnicode_Append
实际上是试图为新对象重用相同的内存位置,in detail在 PyUnicode_Append
:PyUnicode_Append(PyObject **p_left, PyObject *right):
...
new_len = left_len + right_len;
if (unicode_modifiable(left)
...
{
/* append inplace */
if (unicode_resize(p_left, new_len) != 0)
goto error;
/* copy 'right' into the newly allocated area of 'left' */
_PyUnicode_FastCopyCharacters(*p_left, left_len, right, 0, right_len);
}
关于就地for循环中的Python字符串连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69556269/