Mas*_*shy 102 python string reference string-concatenation immutability
Python 中的字符串是不可变的,这意味着值不能更改。但是,当附加到以下示例中的字符串时,由于 id 保持不变,因此原始字符串内存看起来已被修改:
>>> s = 'String'
>>> for i in range(5, 0, -1):
... s += str(i)
... print(f"{s:<11} stored at {id(s)}")
...
String5 stored at 139841228476848
String54 stored at 139841228476848
String543 stored at 139841228476848
String5432 stored at 139841228476848
String54321 stored at 139841228476848
Run Code Online (Sandbox Code Playgroud)
相反,在以下示例中,id 发生变化:
>>> a = "hello"
>>> id(a)
139841228475760
>>> a = "b" + a[1:]
>>> print(a)
bello
>>> id(a)
139841228475312
Run Code Online (Sandbox Code Playgroud)
Sha*_*ger 124
这是针对 CPython 特定的优化,适用于str附加到的对象恰好没有其他活动引用的情况。在这种情况下,解释器“作弊”,允许它通过重新分配(可以就地,取决于堆布局)和直接附加数据来修改现有字符串,并且通常会显着减少重复连接的循环中的工作(使其其行为更像是O(1)a 的摊销追加list,而不是O(n)每次的复制操作)。除了未更改之外,它没有任何可见的效果id,因此这样做是合法的(除非在逻辑上被替换str,否则任何现有对 a 的引用的人都不会看到它发生变化str)。
你实际上不应该依赖它(非引用计数解释器不能使用这个技巧,因为它们不知道是否有str其他引用),根据PEP8 的第一个编程建议:
\n\n代码的编写方式不应损害 Python 的其他实现(PyPy、Jython、IronPython、Cython、Psyco 等)。
\n
\n\n\n
a += b例如,不要依赖 CPython\xe2\x80\x99s 对或形式的语句进行就地字符串连接的高效实现a = a + b。即使在 CPython 中,这种优化也是脆弱的(它仅适用于某些类型),并且在不使用引用计数的实现中根本不存在 xe2x80x99。在库的性能敏感部分,\'\'.join()应改用表单。这将确保在各种实现中串联在线性时间内发生。
如果你想打破优化,有多种方法可以做到这一点,例如将代码更改为:
\n>>> while i!=0:\n... s_alias = s # Gonna save off an alias here\n... s += str(i)\n... print(s + " stored at " + str(id(s)))\n... i -= 1\n... \nRun Code Online (Sandbox Code Playgroud)\n通过创建别名、增加引用计数并告诉 Python 更改将在 以外的其他地方可见s,因此它无法应用它,从而打破它。同样,代码如下:
s = s + a + b\nRun Code Online (Sandbox Code Playgroud)\n无法使用它,因为s + a首先发生,并产生一个b必须添加到的临时值,而不是立即替换s,并且优化太脆弱,无法尝试处理该问题。几乎相同的代码,例如:
s += a + b\nRun Code Online (Sandbox Code Playgroud)\n或者:
\ns = s + (a + b)\nRun Code Online (Sandbox Code Playgroud)\n通过确保最终串联始终是左操作数之一来恢复优化s,并且结果用于立即替换s。
Mar*_*nen 44
无论实现细节如何,文档都说:
\n\n\n\xe2\x80\xa6 具有不重叠生命周期的两个对象可能具有相同的 id() 值。
\n
在之后 引用的前一个对象s不再存在,+=因此新对象具有相同的 不会违反规则id。
| 归档时间: |
|
| 查看次数: |
6958 次 |
| 最近记录: |