>>b="wtf">>>aisbTrue# example2:>>>a="wtf!">>>b="wtf!">>>aisbFalse# example3:>..._为啥我的python字符串不驻留啊">
赞
踩
本文整理了许多字符串驻留的坑,部分整合自wtfpython英文版,并增加了大量的后续说明。
# example1:
>>>a="wtf"
>>>b="wtf"
>>>aisb
True
# example2:
>>>a="wtf!"
>>>b="wtf!"
>>>aisb
False
# example3:
>>>a,b="wtf!","wtf!"
>>>aisb
True# 3.7 版本返回结果为 False.
# example4:
>>>'a'*20is'aaaaaaaaaaaaaaaaaaaa'
True
>>>'a'*21is'aaaaaaaaaaaaaaaaaaaaa'
False# 3.7 版本返回结果为 True
字符串的这些问题,像是在和你说 1 != 1 一样坑爹。
究其原因,其实是CPython在编译的时候会自动进行优化,在某些情况下它会尝试使用已经存在的不可变对象,而不是创建一个新的对象,而恰好,字符串就是不可变对象。这种使用已存在的不可变对象的行为被称为“驻留 ” 。
驻留的原本设计意图是用于节省内存的,但是确实有时候会坑到程序员。怎样判断自己的字符串会否被驻留呢?请看这份代码:https://github.com/python/cpython/blob/3.6/Objects/codeobject.c#L19
简单地来讲:
1.所有长度为0和1的字符串都会被驻留
2.字符串在编译时被实现的会被驻留(如'wtf'会被驻留,但是 ''.join(['w', 't', 'f']) 不会)
3.字符串中只包含ASCII下的字母、数字和下划线时会被驻留. 所以'wtf!'由于包含!不会被驻留。
我们的example1中,由于发生了驻留,所以a和b是同一个字符串对象。而example2中,由于没有发生字符串驻留,a="wtf!"和b="wtf!"实际上使用的不是同一个字符串对象,你可以使用id获得对象的唯一标志,你会发现它们的不同:
a和b都为wtf!时:
>>>a="wtf!"
>>>b="wtf!"
>>>aisb
False
>>>a==b
True
>>>id(a)
2272774097864
>>>id(b)
2272774097024
再来看看没有发生驻留时的情况,a和b都为wtf时:
# a和b都为wtf
>>>a="wtf"
>>>b="wtf"
>>>aisb
True
>>>a==b
True
>>>id(a)
2272774096744
>>>id(b)
2272774096744
明白了吧?如果你想从结果识别对象是否发生驻留,关键就看对象的唯一标志有没有被改变。
不过,如example3所示,当你在同一行中将a和b都设置为 wtf! 的时候,Python解释器会创建一个新的对象,然后同时引用第二个变量,这时候它两的唯一标志id就是一样的。(example3仅适用于python3.7以下,后面被改了)。
example4中,发生了常量折叠,这其实也是一种优化技术。编译时表达式 'a'*20 会被替换成 'aaaaaaaaaaaaaaaaaaaa' (不要数了,20个),不过只有长度小于20的字符串才会发生常量替换,这就是为什么 'a'*21并不等于 'aaaaaaaaaaaaaaaaaaaaa' (不要数了,21个) 。
好,感谢大家的阅读,今天的.....等等,你以为这就结束了吗?还有呢:
>>>a=10
>>>b=10
>>>aisb
True
>>>a=256
>>>b=256
>>>aisb
True
>>>a=257
>>>b=257
>>>aisb
False
这又是为啥啊?
请注意,Python中,对于整数对象,如果其值处于[-5,256]的闭区间内,则值相同的对象是同一个对象,否则为不同对象。我知道你想问,别问,问就是源码本身就这么写的。
(其实主要还是从性能方面考虑,-5到256这段数值被经常使用,因此干脆设为同一个对象重复使用,避免分配空间—赋予类别—赋予初始值等一系列操作)。
如果你喜欢今天的Python 教程,请持续关注Python实用宝典,如果对你有帮助,麻烦在下面点一个赞/在看
,有任何问题都可以在下方留言,我们会耐心解答的!
点击下方阅读原文可以获取所有代码和链接哦!
Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。