当前位置: 首页 > 知识库问答 >
问题:

如果Python字符串是不可变的,如果我使用=来附加它,它如何保持相同的id?

太叔小云
2023-03-14

Python中的字符串是不可变的,这意味着该值不能更改。我正在测试该场景,但看起来原始字符串已被修改。

>>> 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
>>> a = "hello"
>>> id(a)
139841228475760
>>> a = "b" + a[1:]
>>> print(a)
bello
>>> id(a)
139841228475312

共有2个答案

陶鸿畴
2023-03-14

无论实施细节如何,这些文件都说:

…两个生命周期不重叠的对象可能具有相同的id()值。

s引用的前一个对象在=之后不再存在,因此新对象不会因为具有相同的id而违反任何规则。

岳英锐
2023-03-14

当所附加的str恰好没有其他活引用时,这是一个特定于CPython的优化。在这种情况下,解释器“作弊”,允许它通过重新分配(根据堆布局,可以在适当的位置)和直接附加数据来修改现有字符串,而且经常会显著减少重复串联的循环中的工作量(使其更像是列表的摊销附件,而不是每次复制操作)。除了未更改的id之外,它没有任何可见的效果,因此这样做是合法的(除非逻辑上替换了str,否则任何人都不会看到它的更改)。

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

代码的编写方式应不影响其他Python实现(PyPy、Jython、IronPython、Cython、Psyco等)。

例如,不要依赖CPython对形式为a=ba=ab的语句的就地字符串串联的高效实现。这种html" target="_blank">优化即使在CPython中也很脆弱(它只适用于某些类型),并且在不使用refcounting的实现中根本不存在。在库中对性能敏感的部分中。应改用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 a首先出现,并产生一个临时的b然后必须添加到其中,而不是立即替换s,并且优化太脆弱而无法尝试处理它。几乎相同的代码如下:

s += a + b

或:

s = s + (a + b)

通过确保最终串联始终为左操作数,并使用结果立即替换左操作数来恢复优化。

 类似资料: