我试图找出哪些整数 python 只实例化一次(似乎是 -6 到 256),在此过程中偶然发现了一些我看不出模式的字符串行为。有时,以不同方式创建的相等字符串共享相同的 id,有时则不共享。此代码:
A = "10000" B = "10000" C = "100" + "00" D = "%i"%10000 E = str(10000) F = str(10000) G = str(100) + "00" H = "0".join(("10","00")) for obj in (A,B,C,D,E,F,G,H): print obj, id(obj), obj is A
印刷:
10000 4959776 True 10000 4959776 True 10000 4959776 True 10000 4959776 True 10000 4959456 False 10000 4959488 False 10000 4959520 False 10000 4959680 False
我甚至看不出其中的规律——除了前四个没有显式的函数调用——但肯定不是这样,因为+例如 C 中的“”意味着对add 的函数调用。我尤其不明白为什么 C 和 G 不同,因为这意味着加法成分的 id 比结果更重要。
+
那么,AD 经历了什么特殊处理,使得它们成为同一个实例?
在 Python 中,字符串的行为可以部分归因于 字符串驻留(interning) 机制。驻留会导致某些字符串被“缓存”并重用以优化性能。这种缓存通常适用于短字符串和某些纯数字或标识符形式的字符串。驻留机制的具体行为在不同 Python 版本和实现中可能略有不同,但一般会应用在编译时确定的字符串常量上。
在您的示例中,Python 为一些字符串对象提供驻留,导致共享相同的 id,而其他字符串则没有被驻留,因此每个生成的字符串都分配了一个新的 id。具体分析如下:
id
直接赋值的字符串常量:像 A 和 B,它们是字符串字面量 "10000",在 Python 编译时创建。这些字符串被驻留,所以 A 和 B 指向相同的对象并共享相同的 id。
A
B
"10000"
拼接的字符串常量:对于 C ("100" + "00"),Python 在编译时就知道这两个字符串是常量,因此会将它们优化为一个单独的驻留字符串,这使得 C 也与 A 和 B 指向相同的对象。
C
"100" + "00"
格式化和动态生成的字符串:例如 D ("%i" % 10000),以及通过 str() 函数创建的 E 和 F,这些字符串是在运行时生成的,不会被驻留。由于没有驻留,它们在内存中创建了独立的对象,导致不同的 id。
D
"%i" % 10000
str()
E
F
动态拼接的字符串:像 G (str(100) + "00") 和 H ("0".join(("10", "00"))) 也是在运行时生成的,因此不会被驻留,这意味着它们是新的对象并且拥有不同的 id。
G
str(100) + "00"
H
"0".join(("10", "00"))
因此,关键区别在于: - 编译时常量(如直接字面量或拼接的字面量)可能会被驻留,导致它们共享 id。 - 运行时动态生成的字符串(例如使用 str()、% 格式化、join 等)通常不会被驻留,每个生成的字符串是一个新的对象。
%
join
Python 的驻留规则对数字和标识符形式的字符串较为严格,因此并不是所有字符串都适用。对于更短的、简单的字符串,驻留机制更常见,而较长或动态创建的字符串则更少驻留。