卧槽!Python还有这些特性(1):微妙的字符串
(给Python开发者加星标,提升Python技能)
英文:Satwik Kansal,翻译:暮晨
Python开发者 整理自 GitHub
【导读】:Python 是一个设计优美的解释型高级语言,它提供了很多能让程序员感到舒适的功能特性。但有的时候 Python 的一些输出结果对于初学者来说似乎并不是那么一目了然。
GitHub 有个仓库收集 Python 中那些难以理解和反人类直觉的例子以及鲜为人知的功能特性,并尝试讨论这些现象背后真正的原理!
虽然有些例子并不一定会让你觉得“卧槽”(WTF),但它们依然有可能会告诉你一些你所不知道的 Python 有趣特性。我觉得这是一种学习编程语言内部原理的好办法,而且我相信你也会从中获得乐趣!
如果你是一位经验比较丰富的 Python 程序员,你可以尝试挑战看是否能一次就找到例子的正确答案。你可能对其中的一些例子已经比较熟悉了,那这也许能唤起你当年踩这些坑时的甜蜜回忆。
今天我们的主角是字符串,让我们开始吧!
微妙的字符串
1.
>>> a = "some_string"
>>> id(a)
140420665652016
>>> id("some" + "_" + "string") # 注意两个的id值是相同的.
140420665652016
2.
>>> a = "wtf"
>>> b = "wtf"
>>> a is b
True
>>> a = "wtf!"
>>> b = "wtf!"
>>> a is b
False
>>> a, b = "wtf!", "wtf!"
>>> a is b
True # 3.7 版本返回结果为 False.
3.
>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False # 3.7 版本返回结果为 True
很好理解, 对吧?
说明:
这些行为是由于 Cpython 在编译优化时, 某些情况下会尝试使用已经存在的不可变对象而不是每次都创建一个新对象. (这种行为被称作字符串的驻留[string interning])
发生驻留之后, 许多变量可能指向内存中的相同字符串对象. (从而节省内存)
在上面的代码中, 字符串是隐式驻留的. 何时发生隐式驻留则取决于具体的实现. 这里有一些方法可以用来猜测字符串是否会被驻留:
所有长度为 0 和长度为 1 的字符串都被驻留.
字符串在编译时被实现 (
'wtf'
将被驻留, 但是''.join(['w', 't', 'f'])
将不会被驻留)字符串中只包含字母,数字或下划线时将会驻留. 所以
'wtf!'
由于包含!
而未被驻留. 可以在这里[1]找到 CPython 对此规则的实现.当在同一行将
a
和b
的值设置为"wtf!"
的时候, Python 解释器会创建一个新对象, 然后同时引用第二个变量(译: 仅适用于3.7以下, 详细情况请看这里[2]). 如果你在不同的行上进行赋值操作, 它就不会“知道”已经有一个wtf!
对象 (因为"wtf!"
不是按照上面提到的方式被隐式驻留的). 它是一种编译器优化, 特别适用于交互式环境.常量折叠(constant folding) 是 Python 中的一种 窥孔优化(peephole optimization)[3] 技术. 这意味着在编译时表达式
'a'*20
会被替换为'aaaaaaaaaaaaaaaaaaaa'
以减少运行时的时钟周期. 只有长度小于 20 的字符串才会发生常量折叠. (为啥? 想象一下由于表达式'a'*10**10
而生成的.pyc
文件的大小). 相关的源码实现在这里[4].如果你是使用 3.7 版本中运行上述示例代码, 会发现部分代码的运行结果与注释说明相同. 这是因为在 3.7 版本中, 常量折叠已经从窥孔优化器迁移至新的 AST 优化器, 后者可以以更高的一致性来执行优化. (由 Eugene Toder 和 INADA Naoki 在 bpo-29469[5] 和 bpo-11549[6] 中贡献.)
(但是在最新的 3.8 版本中, 结果又变回去了. 虽然 3.8 版本和 3.7 版本一样, 都是使用 AST 优化器. 目前不确定官方对 3.8 版本的 AST 做了什么调整.)
参考资料
https://github.com/python/cpython/blob/3.6/Objects/codeobject.c#L19
[2]https://github.com/leisurelicht/wtfpython-cn/issues/13
[3]窥孔优化(peephole optimization): https://en.wikipedia.org/wiki/Peephole_optimization
[4]https://github.com/python/cpython/blob/3.6/Python/peephole.c#L288
[5]bpo-29469: https://bugs.python.org/issue29469
[6]bpo-11549: https://bugs.python.org/issue11549
- EOF -
觉得本文对你有帮助?请分享给更多人
推荐关注「Python开发者」,提升Python技能
点赞和在看就是最大的支持❤️