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
:
- 将变量设置为值
"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 字符串是不可变的,因此(通常)任何 对字符串执行的操作 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 一个新字符串。
考虑下面的代码。它的输出是
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]
更改 ida[:1]
不改变idg(a)
不改变idf(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 字符串是不可变的,因此(通常)任何 对字符串执行的操作 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 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 一个新字符串。