详解python中的 is 操作符


Posted in Python onDecember 26, 2017

大家可以与Java中的 == 操作符相互印证一下,加深一下对引用和对象的理解。原问题: Python为什么直接运行和在命令行运行同样语句但结果却不同,他们的缓存机制不同吗?

其实,高票答案已经说得很详细了。我只是再补充一点而已。

is 操作符是Python语言的一个内建的操作符。它的作用在于比较两个变量是否指向了同一个对象。

与 == 的区别

class A():
 def __init__(self, v): 
  self.value = v
 def __eq__(self, t): 
  return self.value == t.value
a = A(3)
b = A(3)
print a == b
print a is b

这个结果是True,False。因为我们重写了__eq__方法就使得a, b在比较的时候,只比较它们的value即可。只要它们的value相等,那么a, b就是相等的。

而 is 操作符是判断两个变量是否引用了同一个对象。

同一个对象?

is 的用法说起来其实挺简单的,但是真正用起来,它的难点恰恰就在于判断哪些对象是同一个对象。

看下面的几个测试,先不看结果,自己能答对多少?

a = 10
b = 10
print a is b
a = 10.0
b = 10.0
print a is b
a = 10
def f():
 return 10
print f() is a
a = 1000
def f():
 return 1000
print f() is a
a = 10.0
def f():
 return 10.0
print f() is a

嗯。这个结果是True, True, True, False, False。你答对了吗?

这个结果中牵扯到两个问题:第一,就是小整数的缓存,第二,就是pyc文件中CodeObject的组织问题。

Python中把-127到128这些小整数都缓存了一份。这和Java的Integer类是一样的。所以,对于-127到128之间的整数,整个Python虚拟机中就只有一个实例。不管你什么时候,什么场景下去使用 is 进行判断,都会是True,所以我们知道了这两个测试一定会是True:

a = 10
b = 10
print a is b
a = 10
def f():
 return 10
print f() is a

接着,我们重点看下,这两个测试:

a = 10.0
b = 10.0
print a is b
a = 10.0
def f():
 return 10.0
print f() is a

为什么一个是True,一个是False。要探究这个问题,就要从字节码的角度去分析了。我们先把这个文件编译一下:

python -m compileall testis.py

然后再使用这个工具查看一下字节码文件: 

  https:// github.com/hinus/railgu n/blob/master/src/main/python/rgparser/show.py

得到这样的输出:

<argcount> 0 </argcount>
 <nlocals> 0</nlocals>
 <stacksize> 2</stacksize>
 <flags> 0040</flags>
 <code>
  6400005a00006400005a01006500006501006b080047486400005a000064
  01008400005a02006502008300006500006b0800474864020053
 </code>
 <dis>
 1   0 LOAD_CONST    0 (10.0)
    3 STORE_NAME    0 (a)

 2   6 LOAD_CONST    0 (10.0)
    9 STORE_NAME    1 (b)

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

 5   23 LOAD_CONST    0 (10.0)
    26 STORE_NAME    0 (a)

 6   29 LOAD_CONST    1 (<code object f>)
    32 MAKE_FUNCTION   0
    35 STORE_NAME    2 (f)

 8   38 LOAD_NAME    2 (f)
    41 CALL_FUNCTION   0
    44 LOAD_NAME    0 (a)
    47 COMPARE_OP    8 (is)
    50 PRINT_ITEM   
    51 PRINT_NEWLINE  
    52 LOAD_CONST    2 (None)
    55 RETURN_VALUE  
 </dis>
 <names> ('a', 'b', 'f')</names>
 <varnames> ()</varnames>
 <freevars> ()</freevars>
 <cellvars> ()</cellvars>
 <filename> 'testis.py'</filename>
 <name> '<module>'</name>
 <firstlineno> 1</firstlineno>
 <consts>
  10.0
  <code>
   <argcount> 0 </argcount>
   <nlocals> 0</nlocals>
   <stacksize> 1</stacksize>
   <flags> 0043</flags>
   <code> 64010053</code>
   <dis>
 7   0 LOAD_CONST    1 (10.0)
    3 RETURN_VALUE  
   </dis>
   <names> ()</names>
   <varnames> ()</varnames>
   <freevars> ()</freevars>
   <cellvars> ()</cellvars>
   <filename> 'testis.py'</filename>
   <name> 'f'</name>
   <firstlineno> 6</firstlineno>
   <consts>
   None
   10.0
   </consts>
   <lnotab> 0001</lnotab>
  </code>
  None
 </consts>
 <lnotab> 060106010b0206010902</lnotab>

大家注意看,整个python文件其实就是一个大的<code>对象,f 所对应的那个函数也是一个<code>对象,这个code对象做为整体是大的<code>对象的consts域里的一个const项。再注意,在大<code>对象里,有10.0这样的一个const项,f 这个<code>对象所对应的conts里呢,也有一个10.0这个浮点数。

当python在加载这个文件的时候,就会完成主<code>里的10.0这个浮点数的加载,生成一个PyFloatObject。也就是说静态的pyc文件的常量表在被加载以后,就变成了内存中的常量表,文件的表里的10.0就变成了内存中的一个PyFloatObject。所以,a, b两个变量都会引用这个PyFloatObject。

但是 f 里的那个10.0呢?它是要等到MAKE_FUNCTION被调用的时候才会真正地初始化。做为 f 方法的返回值,它必然与我们之前所说的主<code>里的10.0不是同一个对象了。

本质上讲,这是Python的一个设计缺陷(例如Java以一个文件为编译单元,共享同一个常量池就会减轻这个问题。但如果跨文件使用 == 操作符,也会出现同样的问题。仍然没有解决这个问题。实际上,我自己也不知道该怎么解决这个问题。)我们应该尽量避免 is 的这种用法。始终把 is 的用法限制在本文的第一个例子中。这样相对会安全一些。

Python 相关文章推荐
一篇文章入门Python生态系统(Python新手入门指导)
Dec 11 Python
python使用matplotlib绘制折线图教程
Feb 08 Python
Python实现树的先序、中序、后序排序算法示例
Jun 23 Python
python flask 多对多表查询功能
Jun 25 Python
Python实现上下班抢个顺风单脚本
Feb 07 Python
python按行读取文件,去掉每行的换行符\n的实例
Apr 19 Python
对numpy中的数组条件筛选功能详解
Jul 02 Python
python实现感知器算法(批处理)
Jan 18 Python
Python自动化之数据驱动让你的脚本简洁10倍【推荐】
Jun 04 Python
TensorFlow2.X使用图片制作简单的数据集训练模型
Apr 08 Python
Python通过队列来实现进程间通信的示例
Oct 14 Python
Python3+PyCharm+Django+Django REST framework配置与简单开发教程
Feb 16 Python
matplotlib简介,安装和简单实例代码
Dec 26 #Python
Python中xrange与yield的用法实例分析
Dec 26 #Python
Python简单计算数组元素平均值的方法示例
Dec 26 #Python
Python爬虫获取整个站点中的所有外部链接代码示例
Dec 26 #Python
Python之web模板应用
Dec 26 #Python
通过python+selenium3实现浏览器刷简书文章阅读量
Dec 26 #Python
如何在python中使用selenium的示例
Dec 26 #Python
You might like
CI框架整合smarty步骤详解
2016/05/19 PHP
PHP与服务器文件系统的简单交互
2016/10/21 PHP
php curl上传、下载、https登陆实现代码
2017/07/23 PHP
javascript支持firefox,ie7页面布局拖拽效果代码
2007/12/20 Javascript
jQuery 使用手册(五)
2009/09/23 Javascript
自己写的Javascript计算时间差函数
2013/10/28 Javascript
一个js过滤空格的小函数
2014/10/10 Javascript
Angular用来控制元素的展示与否的原生指令介绍
2015/01/07 Javascript
移动手机APP手指滑动切换图片特效附源码下载
2015/11/30 Javascript
实例讲解避免javascript冲突的方法
2016/01/03 Javascript
详解JavaScript实现设计模式中的适配器模式的方法
2016/05/18 Javascript
JavaScript中的对象和原型(一)
2016/08/12 Javascript
vue.js树形组件之删除双击增加分支实例代码
2017/02/28 Javascript
详解 vue.js用法和特性
2017/10/15 Javascript
jQuery判断网页是否已经滚动到浏览器底部的实现方法
2017/10/27 jQuery
Vue.js实现可排序的表格组件功能示例
2019/02/19 Javascript
[59:48]DOTA2-DPC中国联赛 正赛 VG vs Magma BO3 第一场 1月26日
2021/03/11 DOTA
Python中多线程thread与threading的实现方法
2014/08/18 Python
机器学习python实战之手写数字识别
2017/11/01 Python
python中ASCII码字符与int之间的转换方法
2018/07/09 Python
python进程池实现的多进程文件夹copy器完整示例
2019/11/27 Python
对python中 math模块下 atan 和 atan2的区别详解
2020/01/17 Python
python opencv pytesseract 验证码识别的实现
2020/08/28 Python
python 利用Pyinstaller打包Web项目
2020/10/23 Python
Bata印度官网:源自欧洲舒适鞋履品牌
2020/01/30 全球购物
环境科学毕业生自荐信
2013/11/21 职场文书
应届毕业生通用的自荐书范文
2014/02/07 职场文书
贯彻学习两会心得体会范文
2014/03/17 职场文书
助人为乐好少年事迹材料
2014/08/18 职场文书
乡镇领导班子四风对照检查材料
2014/09/27 职场文书
2014年教师德育工作总结
2014/11/10 职场文书
2014年幼师工作总结
2014/11/22 职场文书
大学生创业事迹材料
2014/12/30 职场文书
应届毕业生的自我评价
2019/06/21 职场文书
关于React Native 无法链接模拟器的问题
2021/06/21 Javascript
Tomcat项目启动失败的原因和解决办法
2022/04/20 Servers