python如何将id分配给字符串?



考虑下面的代码。它的输出是

1 385712698864 385712698864
2 385744287024
3 385744287088
4 385712698864
5 385744286960
6 385744286960
7 385744286960
8 385712698864

这意味着下面代码中的一些操作会改变id,但有些操作不会,即使没有操作改变变量a的值:

  • 将变量值设置为"a"总是会产生相同的id(在这个特定的运行中,是385712698864)
  • 使用a.lower()在每次呼叫后改变a的id
  • a[::-1]更改id
  • a[:1]不改变id
  • g(a)不改变id
  • f(a)更改id

有人能解释一下这种看似不一致的行为吗?(我使用python 3.8)

代码:

def f(x):
y = x + x
n = len(x)
return y[:n]

def g(x):
return "" + x

a = "a"
b = "a"
print(1, id(a), id(b))
a = a.lower()
print(2, id(a))
a = a.lower()
print(3, id(a))
a = "a"
print(4, id(a))
a = a[::-1]
print(5, id(a))
a = a[:1]
print(6, id(a))
a = g(a)
print(7, id(a))
a = f(a)
print(8, id(a))

Python字符串是不可变的,因此(一般情况下)任何对字符串执行的操作都会返回一个新字符串。作为CPython(标准Python实现)的实现细节,id(x)通常返回x的内存地址。有时,Python解释器很容易识别在哪里可以重用现有字符串并节省一些内存(这被称为"实习",并在Python中其他不可变类型的上下文中讨论),在这些情况下,'两个'字符串将具有相同的id

以一个相等的字符串赋值给两个不同的变量为例。解释器足够聪明,可以缓存文字字符串值(即令牌"a"),并在内存中使用相同的字符串来表示这些值。这很好,因为无论如何都不能改变字符串,也不会有发生意外的危险。

您可以在示例1和示例4中看到这种实习:因为解释器已经缓存了"a",所以它们被赋予相同的ID:

a = "a" * 20
b = "a" * 20
assert id(a) == id(b)  # True

对于较长的字符串,这种行为不会发生:

a = "a" * 10_000
b = "a" * 10_000
assert id(a) == id(b)  # raises AssertionError

如果您使用变量来更改字符串的长度,也不会发生这种情况,因为解析器不太明显,这将导致相同的字符串:

>>> n = 20
>>> a = "a" * n
>>> b = "a" * n
>>> assert id(a) == id(b)  # raises AssertionError

在另外两种情况下(6和7),您不会对字符串的长度或排列造成任何改变:

  • string[:len(string)]优化到string
  • 添加空字符串不会改变现有字符串

解释器能够将这些优化为无操作。

在示例5和8中,解释器不可能在不实际执行操作的情况下知道字符串是否会被更改(例如,我们知道a[::-1] == a,但检查它所需要的工作量与创建一个新字符串一样多!),因此它将返回一个新字符串。

最新更新