关于python中字符串实例唯一性的问题

Mar*_*kus 3 python string instance uniqueidentifier

我试图弄清楚哪些整数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
Run Code Online (Sandbox Code Playgroud)

打印:

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中的" "意味着要添加的函数调用.我特别不明白为什么C和G是不同的,因为这意味着加法的组成部分的ID比结果更重要.

那么,AD经历的特殊待遇是什么,使它们成为同一个例子?

Ale*_*lli 10

在语言规范方面,对于任何不可变类型的实例,任何兼容的Python编译器和运行时都可以完全允许创建新实例或查找与所需值相同的现有实例,并使用新的引用那个相同的例子.这意味着is在不可变项之间使用或通过比较总是不正确的,并且任何次要版本都可能在这个问题上调整或改变策略以增强优化.

在实现方面,权衡非常清楚:尝试重用现有实例可能意味着花费时间(可能是浪费)试图找到这样的实例,但如果尝试成功则会节省一些内存(以及分配时间)然后释放保存新实例所需的内存位.

如何解决这些实现权衡并不是完全明显的 - 如果你能够识别出表明找到合适的现有实例的启发式方法并且搜索(即使它失败)会很快,那么你可能想要尝试搜索和 - 当启发式建议时,重复使用,但跳过它.

在你的观察中,你似乎找到了一个特定的点发布实现,当它完全安全,快速和简单时执行一点点窥视孔优化,所以赋值A到D都归结为与A完全相同(但是E到F不会,因为它们涉及命名函数或方法,优化器的作者可能合理地认为不是100%安全地假设语义 - 如果完成则低ROI - 因此它们不是窥视孔优化的).

因此,重复使用相同实例的A到D归结为A和B这样做(因为C和D将窥孔优化到完全相同的构造).

反过来,这种重用清楚地表明了编译器策略/优化器启发式,其中同一函数的本地命名空间中的不可变类型的相同文字常量被折叠为仅引用函数中的一个实例.func_code.co_consts(使用当前CPython的函数和代码属性的术语)对象) - 合理的策略和启发式,因为在一个函数中重复使用相同的不可变常量文字有点频繁,并且价格只支付一次(在编译时),而优势累计多次(每次函数运行时,也许在循环等等).

(碰巧这些具体的策略和启发式方法,鉴于它们明显积极的权衡,在所有最新版本的CPython中都很普遍,而且我相信,IronPython,Jython和PyPy也是如此;-).

如果您计划为Python本身或类似语言编写编译器,运行时环境,窥孔优化器等,那么这是一个有点值得研究的有趣内容.我想深入研究内部结构(理想情况下是许多不同的正确实现,当然,为了不注意特定的一个怪癖 - Python目前享有至少4个独立的生产价值实现的优点,更不用说了每个版本的几个版本!)也可以间接地帮助一个更好的Python程序员 - 但是关注语言本身所保证的内容尤其重要,这比你在单独的实现中发现的要少一些,因为现在"刚刚发生"的部分(在语言规范中没有要求)可能会在下一个版本的一个或另一个实现中完全改变,如果你的生产代码是错误的依赖这些细节,可能会引起令人讨厌的惊喜;-).另外 - 依靠这样的变量实现细节而不是语言强制行为几乎没有必要,甚至特别有用(除非你编写类似于优化器,调试器,分析器等的东西;当然; ).