我试图理解Python字符串何时相同(也就是共享相同的内存位置)。但是在我的测试中,当两个相等的字符串变量共享相同的内存时似乎没有明显的解释:
import sys
print(sys.version) # 3.4.3
# Example 1
s1 = "Hello"
s2 = "Hello"
print(id(s1) == id(s2)) # True
# Example 2
s1 = "Hello" * 3
s2 = "Hello" * 3
print(id(s1) == id(s2)) # True
# Example 3
i = 3
s1 = "Hello" * i
s2 = "Hello" * i
print(id(s1) == id(s2)) # False
# Example 4
s1 = "HelloHelloHelloHelloHello"
s2 = "HelloHelloHelloHelloHello"
print(id(s1) == id(s2)) # True
# Example 5
s1 = "Hello" * 5
s2 = "Hello" * 5
print(id(s1) == id(s2)) # False
字符串是不可变的,据我所知,Python试图重用现有的不可变对象,让其他变量指向它们而不是在内存中用相同的值创建新对象。
考虑到这一点,Example 1
似乎很明显会返回True
对我来说,Example 2
返回True
仍然是显而易见的。
对我来说,Example 3
返回False
并不明显 - 我是不是和Example 2
做同样的事情?!?
我偶然发现了这个问题:
Why does comparing strings in Python using either '==' or 'is' sometimes produce a different result?
并通读http://guilload.com/python-string-interning/(虽然我可能根本不理解)并且你好 - 好吧,也许“实习”字符串取决于长度,所以我在HelloHelloHelloHelloHello
中使用了Example 4
}。结果是True
。
令我感到困惑的是,与Example 2
中的数字相同,只是使用更大的数字(但它会有效地返回与Example 4
相同的字符串) - 但这次结果是{{ 1}}?!?
我真的不知道Python如何决定是否使用相同的内存对象,或何时创建新内存对象。
是否可以解释这种行为的官方来源?
答案 0 :(得分:9)
避免使用大型.pyc文件
那么
'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
为何不评估为True
?你还记得你在所有软件包中遇到的.pyc文件吗?好吧,Python字节码存储在这些文件中。如果有人写了这样的['foo!'] * 10**9
,会发生什么?生成的 .pyc 文件将是巨大的!为了避免这种现象,如果它们的长度超过20,则通过窥孔优化产生的序列被丢弃。
如果您有字符串"HelloHelloHelloHelloHello"
,Python必须按原样存储它(要求解释器检测字符串中的重复模式以节省空间可能太多)。但是,当涉及到可以在解析时计算的字符串值时,例如"Hello" * 5
,Python会将这些值作为所谓的“#peephole optimization"”的一部分进行评估,这可以决定它是否是值得或不值得预先计算字符串。从len("Hello" * 5) > 20
开始,解释器将它保留为避免存储太多长字符串。
编辑:
如this question所示,您可以在peephole.c
,功能fold_binops_on_constants
中的源代码中查看此内容,接近结尾您将看到:
// ...
} else if (size > 20) {
Py_DECREF(newconst);
return -1;
}
编辑2:
实际上,最近这个优化代码已经moved to the AST optimizer用于Python 3.7,所以现在你必须调查ast_opt.c
,函数fold_binop
,它们调用现在函数safe_multiply
,它检查字符串是否不超过MAX_STR_SIZE
,newly defined as 4096。因此,下一版本的限制似乎已大大提升。
答案 1 :(得分:1)
在示例2中:
# Example 2
s1 = "Hello" * 3
s2 = "Hello" * 3
print(id(s1) == id(s2)) # True
这里在编译时评估s1和s2的值。这将返回true。
在例3中:
# Example 3
i = 3
s1 = "Hello" * i
s2 = "Hello" * i
print(id(s1) == id(s2)) # False
这里s1和s2的值是在运行时计算的,结果不是自动中断的,所以这将返回false。这是为了避免在运行时自己创建“HelloHelloHello”字符串来分配过多的内存。
如果你手动实习,它将返回True
i = 3
s1 = "Hello" * i
s2 = "Hello" * i
print(id(intern(s1)) == id(intern(s2))) # True