is 操作符是Python语言的一个内建的操作符。它的作用在于比较两个变量是否指向了同一个对象。
与 == 的区别
这个结果是True,False。因为我们重写了__eq__方法就使得a, b在比较的时候,只比较它们的value即可。只要它们的value相等,那么a, b就是相等的。
而 is 操作符是判断两个变量是否引用了同一个对象。
同一个对象?
is 的用法说起来其实挺简单的,但是真正用起来,它的难点恰恰就在于判断哪些对象是同一个对象。
看下面的几个测试,先不看结果,自己能答对多少?
嗯。这个结果是True, True, True, False, False。你答对了吗?
这个结果中牵扯到两个问题:第一,就是小整数的缓存,第二,就是pyc文件中CodeObject的组织问题。
Python中把-128到127这些小整数都缓存了一份。这和Java的Integer类是一样的。所以,对于-128到127之间的整数,整个Python虚拟机中就只有一个实例。不管你什么时候,什么场景下去使用 is 进行判断,都会是True,所以我们知道了这两个测试一定会是True:
接着,我们重点看下,这两个测试:
为什么一个是True,一个是False。要探究这个问题,就要从字节码的角度去分析了。我们先把这个文件编译一下:
然后再使用这个工具查看一下字节码文件:https://github.com/hinus/railgun/blob/master/src/main/python/rgparser/show.py
得到这样的输出:
大家注意看,整个python文件其实就是一个大的对象,f 所对应的那个函数也是一个对象,这个code对象做为整体是大的对象的consts域里的一个const项。再注意,在大对象里,有10.0这样的一个const项,f 这个对象所对应的conts里呢,也有一个10.0这个浮点数。
当python在加载这个文件的时候,就会完成主里的10.0这个浮点数的加载,生成一个PyFloatObject。也就是说静态的pyc文件的常量表在被加载以后,就变成了内存中的常量表,文件的表里的10.0就变成了内存中的一个PyFloatObject。所以,a, b两个变量都会引用这个PyFloatObject。
但是 f 里的那个10.0呢?它是要等到MAKE_FUNCTION被调用的时候才会真正地初始化。做为 f 方法的返回值,它必然与我们之前所说的主里的10.0不是同一个对象了。
本质上讲,这是Python的一个设计缺陷(例如Java以一个文件为编译单元,共享同一个常量池就会减轻这个问题。但如果跨文件使用 == 操作符,也会出现同样的问题。仍然没有解决这个问题。实际上,我自己也不知道该怎么解决这个问题。)我们应该尽量避免 is 的这种用法。始终把 is 的用法限制在本文的第一个例子中。这样相对会安全一些。
领取专属 10元无门槛券
私享最新 技术干货