Python解惑之整数比较详解


Posted in Python onApril 24, 2017

前言

在 Python 中一切都是对象,毫无例外整数也是对象,对象之间比较是否相等可以用==,也可以用is。

==和is操作的区别是:

  • is比较的是两个对象的id值是否相等,也就是比较俩对象是否为同一个实例对象,是否指向同一个内存地址。
  • ==比较的是两个对象的内容是否相等,默认会调用对象的__eq__()方法。

清楚is和==的区别之后,对此也许你有可能会遇到下面的这些困惑,于是就有了这样一篇文章,试图把Python中一些隐晦的东西趴出来,希望对你有一定的帮助。

我们先来看两段代码:

片段一:

>>> a = 256
>>> b = 256
>>> a == b
True
>>>

片段二:

>>> a = 256
>>> b = 256
>>> a is b
True
>>>

在交互式命令行执行上面两段代码,代码片段一中的a==b返回True很好理解,因为两个对象的值都是256,对于片段二,a is b也返回True,这说明a和b是指向同一个对象的,可以检查一下他们的id值是否相等:

>>> id(a)
8213296
>>> id(b)
8213296
>>>

结果证明他俩的确是同一个对象,指向的是同一个内存地址。那是不是所有的整数对象只要两个对象的值(内容)相等,它们就是同一个实例对象呢?换句话说,对于整数对象只要==返回True,is操作也会返回True吗?

带着这个问题来看下面这两段代码:

片段一:

>>> a = 257
>>> b = 257
>>> a == b
True
>>>

片段二:

>>> a = 257
>>> b = 257
>>> a is b
False
>>>

对于257,a is b返回的竟然是False,结果可能在你的意料之中,也有可能出乎你的意料,但不管怎么,我们还是要刨根问底,找出问题的真相。

解惑一

出于对性能的考虑,Python内部做了很多的优化工作,对于整数对象,Python把一些频繁使用的整数对象缓存起来,保存到一个叫small_ints的链表中,在Python的整个生命周期内,任何需要引用这些整数对象的地方,都不再重新创建新的对象,而是直接引用缓存中的对象。Python把这些可能频繁使用的整数对象规定在范围[-5, 256]之间的小对象放在small_ints中,但凡是需要用些小整数时,就从这里面取,不再去临时创建新的对象。因为257不再小整数范围内,因此尽管a和b的值是一样,但是他们在Python内部却是以两个独立的对象存在的,各自为政,互不干涉。

弄明白第一个问题后,我们继续在Python交互式命令行中写一个函数,再来看下面这段代码:

片段一:

>>> c = 257
>>> def foo():
...  a = 257
...  b = 257
...  print a is b
...  print a is c
... 
>>> foo()
True
False

呃,什么情况,是的,你没看错,片段一中的这段代码 a、b 值都是257的情况下,出现了a is b返回True,而a is c 返回的False,a、b、c的值都为257,为什么会出现不同的结果呢?这对于刚刚好不容易建立起来的认知就被彻底否决了吗,那这段代码中究竟发生了什么?难道解惑一中的结论是错误的吗?

解惑二

A Python program is constructed from code blocks. A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition. Each command typed interactively is a block. A script file (a file given as standard input to the interpreter or specified as a command line argument to the interpreter) is a code block. A script command (a command specified on the interpreter command line with the ‘-c‘ option) is a code block. structure-of-a-program

为了弄清楚这个问题,我们有必要先理解程序代码块的概念。Python程序由代码块构成,代码块作为程序的一个最小基本单位来执行。一个模块文件、一个函数体、一个类、交互式命令中的单行代码都叫做一个代码块。在上面这段代码中,由两个代码块构成,c = 257作为一个代码块,函数foo作为另外一个代码块。Python内部为了将性能进一步的提高,凡是在一个代码块中创建的整数对象,如果存在一个值与其相同的对象于该代码块中了,那么就直接引用,否则创建一个新的对象出来。Python出于对性能的考虑,但凡是不可变对象,在同一个代码块中的对象,只有是值相同的对象,就不会重复创建,而是直接引用已经存在的对象。因此,不仅是整数对象,还有字符串对象也遵循同样的原则。所以 a is b就理所当然的返回True了,而c和a不在同一个代码块中,因此在Python内部创建了两个值都是257的对象。为了验证刚刚的结论,我们可以借用dis模块从字节码的角度来看看这段代码。

>>> import dis
>>> dis.dis(foo)
 2   0 LOAD_CONST    1 (257)
    3 STORE_FAST    0 (a)

 3   6 LOAD_CONST    1 (257)
    9 STORE_FAST    1 (b)

 4   12 LOAD_FAST    0 (a)
    15 LOAD_FAST    1 (b)
    18 COMPARE_OP    8 (is)
    21 PRINT_ITEM   
    22 PRINT_NEWLINE  

 5   23 LOAD_FAST    0 (a)
    26 LOAD_GLOBAL    0 (c)
    29 COMPARE_OP    8 (is)
    32 PRINT_ITEM   
    33 PRINT_NEWLINE  
    34 LOAD_CONST    0 (None)
    37 RETURN_VALUE

可以看出两个257都是从常量池的同一个位置co_consts[1]获取的。

总结

一番长篇大论之后,得出两点结论:

1、小整数对象[-5,256]是全局解释器范围内被重复使用,永远不会被GC回收。

2、同一个代码块中的不可变对象,只要值是相等的就不会重复创建新的对象。似乎这些知识点对日常的工作一点忙也帮不上,因为你根本不会用is来比较两个整数对象的值是否相等。那为什么还要拿出来讨论呢?嗯,程序员学知识,不应该浅尝辄止,要充分发挥死磕到底的精神。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者使用python能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python实现批量下载新浪博客的方法
Jun 15 Python
Python数据结构之双向链表的定义与使用方法示例
Jan 16 Python
python 将数据保存为excel的xls格式(实例讲解)
May 03 Python
基于python进行桶排序与基数排序的总结
May 29 Python
Python求两个圆的交点坐标或三个圆的交点坐标方法
Nov 07 Python
Python File(文件) 方法整理
Feb 18 Python
django 2.2和mysql使用的常见问题
Jul 18 Python
Python3.6实现根据电影名称(支持电视剧名称),获取下载链接的方法
Aug 26 Python
python ctypes库2_指定参数类型和返回类型详解
Nov 19 Python
简单了解Python读取大文件代码实例
Dec 18 Python
Python chardet库识别编码原理解析
Feb 18 Python
python 利用jieba.analyse进行 关键词提取
Dec 17 Python
Python解惑之True和False详解
Apr 24 #Python
Python新手入门最容易犯的错误总结
Apr 24 #Python
Python正则抓取新闻标题和链接的方法示例
Apr 24 #Python
Python的爬虫框架scrapy用21行代码写一个爬虫
Apr 24 #Python
fastcgi文件读取漏洞之python扫描脚本
Apr 23 #Python
批量获取及验证HTTP代理的Python脚本
Apr 23 #Python
深入理解python中的select模块
Apr 23 #Python
You might like
php连接odbc数据源并保存与查询数据的方法
2014/12/24 PHP
laravel 关联关系遍历数组的例子
2019/10/10 PHP
html下载本地
2006/06/19 Javascript
一步一步制作jquery插件Tabs实现过程
2010/07/06 Javascript
点击弹出层外区域关闭弹出层jquery特效示例
2013/08/25 Javascript
jQuery 三击事件实现代码
2013/09/11 Javascript
Javascript中string转date示例代码
2013/11/01 Javascript
jquery原创弹出层折叠效果点击折叠弹出一个层
2014/03/12 Javascript
javascript判断是手机还是电脑访问网页的简单实例分享
2014/06/03 Javascript
jquery 根据name名获取元素的value值
2015/02/27 Javascript
JS实现往下不断流动网页背景的方法
2015/02/27 Javascript
js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式)
2015/11/09 Javascript
如何消除inline-block属性带来的标签间间隙
2016/03/31 Javascript
浅谈vue 单文件探索
2018/09/05 Javascript
Vue slot用法(小结)
2018/10/22 Javascript
JSON字符串操作移除空串更改key/value的介绍
2019/01/05 Javascript
ES6知识点整理之Proxy的应用实例详解
2019/04/16 Javascript
微信小程序列表时间戳转换实现过程解析
2019/10/12 Javascript
解决vue刷新页面以后丢失store的数据问题
2020/08/11 Javascript
[43:58]DOTA2-DPC中国联赛定级赛 LBZS vs SAG BO3第一场 1月8日
2021/03/11 DOTA
PyInstaller的安装和使用的详细步骤
2020/06/02 Python
python实现梯度下降算法的实例详解
2020/08/17 Python
廉价航班、机票和酒店:JustFly
2018/02/07 全球购物
世界上最全面的草药补充剂和顶级品牌维生素网站:HerbsPro
2019/01/20 全球购物
在购买印度民族服饰:Soch
2020/09/15 全球购物
面向对象编程是如何提高软件开发水平的
2014/05/06 面试题
顶岗实习计划书
2014/01/10 职场文书
海南地接欢迎词
2014/01/14 职场文书
大二法学专业职业生涯规划范文
2014/02/12 职场文书
大堂副理的岗位职责范文
2014/02/17 职场文书
如何写自我评价?自我评价写什么好?
2014/03/14 职场文书
统招统分证明
2015/06/23 职场文书
oracle DGMGRL ORA-16603报错的解决方法(DG Broker)
2021/04/06 Oracle
GitHub上77.9K的Axios项目有哪些值得借鉴的地方详析
2021/06/15 Javascript
python自动化操作之动态验证码、滑动验证码的降噪和识别
2021/08/30 Python
唤醒紫霞仙子,携手再游三界!大话手游X《大话西游》电影合作专属剧情任务
2022/04/03 其他游戏