Python如何为一个字符串分配id?

4
考虑下面的代码。它的输出是:
1 385712698864 385712698864
2 385744287024
3 385744287088
4 385712698864
5 385744286960
6 385744286960
7 385744286960
8 385712698864

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

  • 将变量设置为值"a"始终导致相同的id(在特定运行中,该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))
1个回答

3
Python字符串是不可变的,因此(通常情况下)对字符串执行的任何操作都会返回一个新字符串。作为CPython(标准Python实现)的实现细节,id(x)通常返回x的内存地址。有时,Python解释器可以轻松地识别出它可以重复使用现有字符串并节省一些内存(这称为“内部化”} },并在Python中讨论了其他不可变类型的上下文{{link3:在这个SO答案中),在这些情况下,“两个”字符串将具有相同的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,但检查它需要与创建新字符串一样的工作!),因此它将返回一个新字符串。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接