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

How does python assign id to a string?

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

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

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

有人可以解释这种看似不一致的行为吗? (我使用的是 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 字符串是不可变的,因此(通常)任何 对字符串执行的操作 return 一个新字符串。 As an implementation detail of CPython (the standard Python implementation), id(x) usually returns the memory address of x. Sometimes it's trivially easy for the Python interpreter to recognise where it can re-use an existing string and save some memory (this is called 'interning', and is discussed in the context of other immutable types in Python ),在这些情况下 'both' 字符串将具有相同的 id.

以将一个相等的字符串赋值给两个不同的变量为例。解释器足够聪明,可以缓存 literal 字符串值(即标记 "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,但检查它需要的工作与创建一样多无论如何都是一个新字符串!),所以它将 return 一个新字符串。