Python 小坑之字符串驻留

描述

本文整理了许多字符串驻留的坑,部分整合自wtfpython英文版,并增加了大量的后续说明。

  1. # example1:

  2. >>> a ="wtf"

  3. >>> b ="wtf"

  4. >>> a is b

  5. True

  6. # example2:

  7. >>> a ="wtf!"

  8. >>> b ="wtf!"

  9. >>> a is b

  10. False

  11. # example3:

  12. >>> a, b ="wtf!","wtf!"

  13. >>> a is b

  14. True# 3.7 版本返回结果为 False.

  1. # example4:

  2. >>>'a'*20is'aaaaaaaaaaaaaaaaaaaa'

  3. True

  4. >>>'a'*21is'aaaaaaaaaaaaaaaaaaaaa'

  5. 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!时:

  1. >>> a ="wtf!"

  2. >>> b ="wtf!"

  3. >>> a is b

  4. False

  5. >>> a == b

  6. True

  7. >>> id(a)

  8. 2272774097864

  9. >>> id(b)

  10. 2272774097024

再来看看没有发生驻留时的情况,a和b都为wtf时:

  1. # a和b都为wtf

  2. >>> a ="wtf"

  3. >>> b ="wtf"

  4. >>> a is b

  5. True

  6. >>> a == b

  7. True

  8. >>> id(a)

  9. 2272774096744

  10. >>> id(b)

  11. 2272774096744

明白了吧?如果你想从结果识别对象是否发生驻留,关键就看对象的唯一标志有没有被改变。

不过,如example3所示,当你在同一行中将a和b都设置为 wtf! 的时候,Python解释器会创建一个新的对象,然后同时引用第二个变量,这时候它两的唯一标志id就是一样的。(example3仅适用于python3.7以下,后面被改了)。

example4中,发生了常量折叠,这其实也是一种优化技术。编译时表达式 'a'*20 会被替换成 'aaaaaaaaaaaaaaaaaaaa' (不要数了,20个),不过只有长度小于20的字符串才会发生常量替换,这就是为什么 'a'*21并不等于 'aaaaaaaaaaaaaaaaaaaaa' (不要数了,21个) 。

好,感谢大家的阅读,今天的.....等等,你以为这就结束了吗?还有呢:

  1. >>> a =10

  2. >>> b =10

  3. >>> a is b

  4. True

  5. >>> a =256

  6. >>> b =256

  7. >>> a is b

  8. True

  9. >>> a =257

  10. >>> b =257

  11. >>> a is b

  12. False

这又是为啥啊?

请注意,Python中,对于整数对象,如果其值处于[-5,256]的闭区间内,则值相同的对象是同一个对象,否则为不同对象。我知道你想问,别问,问就是源码本身就这么写的。

(其实主要还是从性能方面考虑,-5到256这段数值被经常使用,因此干脆设为同一个对象重复使用,避免分配空间—赋予类别—赋予初始值等一系列操作)。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

>> b ="wtf">>> a is bTrue# example2:>>> a ="wtf!">>> b ="wtf!">>> a is bFalse# example3:>>> a, b ="wtf!","wtf!">>> a is b True# 3.7">
×
20
完善资料,
赚取积分