如果Python字符串是不可变的,为什么如果我使用+=追加到它,它会保持相同的id?

2022/7/6 14:23:28

本文主要是介绍如果Python字符串是不可变的,为什么如果我使用+=追加到它,它会保持相同的id?,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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

相反,在以下示例中,id 会更改:

>>> a = "hello"
>>> id(a)
139841228475760
>>> a = "b" + a[1:]
>>> print(a)
bello
>>> id(a)
139841228475312
118

这是一个特定于CPython的优化,适用于被追加到碰巧没有其他活引用的情况。在这种情况下,解释器“作弊”,允许它通过重新分配(可以就位,具体取决于堆布局)和直接附加数据来修改现有字符串,并且经常在重复连接的循环中显着减少工作(使其行为更像a的摊销追加而不是每次复制操作)。除了未更改的之外,它没有可见的效果,因此这样做是合法的(除非在逻辑上被替换,否则具有现有引用的人都不会看到它发生变化)。strO(1)listO(n)idstrstr

你实际上不应该依赖它(非引用计数的解释器不能使用这个技巧,因为他们不知道是否有其他引用),根据PEP8的第一个编程建议:str

代码的编写方式不应使Python的其他实现(PyPy,Jython,IronPython,Cython,Psyco等)不利。

例如,不要依赖 CPython 为 form 或 中的语句有效实现就地字符串串联。即使在CPython中,这种优化也很脆弱(它仅适用于某些类型),并且在不使用refcounting的实现中根本不存在。在库的性能敏感部分,应改用表单。这将确保在各种实现中以线性时间进行串联。a += ba = a + b''.join()

如果你想打破优化,有各种各样的方法可以做到这一点,例如,将你的代码更改为:

>>> while i!=0:
...     s_alias = s  # Gonna save off an alias here
...     s += str(i)
...     print(s + " stored at " + str(id(s)))
...     i -= 1
... 

通过创建别名,增加引用计数并告诉Python更改将在 其他位置可见来破坏它,因此它无法应用它。类似地,像这样的代码:s

s = s + a + b

不能使用它,因为首先发生,并产生一个临时的,然后必须添加到,而不是立即替换,并且优化太脆而无法尝试处理。几乎相同的代码,如:s + abs

s += a + b

艺术

s = s + (a + b)

通过确保最终串联始终是左操作数的位置,并且结果用于立即替换 来恢复优化。



这篇关于如果Python字符串是不可变的,为什么如果我使用+=追加到它,它会保持相同的id?的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程