Python中 is 和 == 的区别
- Sunny
- 0
今天在项目中遇到了一个令人费解的bug,话不多说,直接贴上代码.
首先,我在main中使用了参数管理,将脚本运行需要的参数从外部引入。
if __name__ == '__main__':
...
parser.add_argument('--method', type = str, help = 'What kind of spectrum you want to see.')
...
args = parser.parse_args()
...
method = args.method
可以看到,这里从外部引入了一个参数--method
,随即将这个参数存入变量method
。然后,我将这个变量传递给一个类,将这个类实例化:
Object = ClassName(..., method = method)
OK,这里看起来也没有问题,然后我将这个传入的变量值储存在成员变量self._method
中,之后用这个成员变量进行方法的匹配,代码如下:
def _select_method(self):
if self._method in ClassName._methods:
try:
if self._method is'method1':
process = methodClass1()
if self._method is 'method2':
process = methodClass2()
if self._method is 'method3':
process = methodClass3()
if self._method is 'method4':
process = methodClass4()
except:
raise OSError('Please check your input!')
else:
raise methodError('No method named {0}, please check your input!'.format(self._method))
return process
需要进行说明的是,在这里所指的methodClass
都是外部定义的类,内部结构一样,但所采用的方法不同。如果输入的参数method
不在预设的self._methods
中,则会抛出异常提示不含这种方法,要求重新检查输入是否正确。
程序到这里就出错了,可是我找了半天并没有发现有什么问题啊,于是我把判断语句的状态打印出来,观察是否如期望输出。
我们都知道,is
和==
的运算结果都是一个bool型变量,返回True
或者False
,理论上我输入的参数为'method1'
时,运算self._method is'method1'
返回的结果会是True
,可实际上呢?
当我把这个运算式的结果打印出来的时候我惊奇的发现,运算的结果是False
!这是为什么呢?
原来,在python中is
的作用是用来判断两个两边是否映射的是同一个变量,是否id相等,或者说是否返回的是同一个实例,而==
比较的则是两边的内容是否一致,并不要求id相等,也不要求内存地址一致,只要内容相等就行,因此上面的代码应该将is改成==
才能达到想要的效果。
深入探究 is 和 == 的区别
运算符 | 说明 |
---|---|
is | 比较两个对象的id值是否相等,也就是比较俩对象是否为同一个实例对象, 是否指向同一个内存地址。 |
== | 比较的是两个对象的内容是否相等,默认会调用对象的__eq__()方法。 |
这是我们通过项目代码的出来的结论,那么具体在什么情况下会凸显出这两者的区别呢?
int型变量
我们先来看下面这段调试记录:
admin@AdmindeiMac > ~ > python3
Python 3.8.8 (default, Apr 13 2021, 12:59:45)
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 1
>>> b = 1
>>> a == b
True
>>> a is b
True
>>> a = 4567
>>> b = 4567
>>> a == b
True
>>> a is b
False
相信上面的这段记录已经让绝大部分的人凌乱了,为什么相同的两段程序会有完全不同的两个输出?为什么a = b = 1
的时候is
和==
可以等价而其他值不行?
通过查阅资料我们得知,python的解释器为了优化运算性能,在内部对变量进行了优化。对int型而言,解释器会在运行时将一部分常用的值储存起来,这个储存的区域叫small_ints
。在随后该python进程的整个生命周期内,这个链表中的任何值被使用,解释器都会将指针指向链表内已经储存的值,而非创建一个新的对象。在python的解释器中,会把数值在[katex][-5, 256][/katex]之间的整数放在这个链表中。
因此我们可以得知,在上面的代码段中,当a = 1
、b = 1
时,虽然变量名不同,但实际指向的内存地址是一样的,即id
一样,所以is
判断的结果返回True
。
但当a = 4567
、b = 4567
时,二者分别开辟了空间用不同的物理地址存放数据,因此is
返回的结果是False
。
那么我们再来看一段代码:
admin@AdmindeiMac > ~ > python3
Python 3.8.8 (default, Apr 13 2021, 12:59:45)
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 300
>>> def test():
... b = 300
... c = 300
... print(a is b)
... print(b is c)
... pass
...
>>> test()
False
True
这又是怎么回事呢?为什么值为300的时候is的返回值又是True
了呢?但是a
和b
判断的结果却返回的是False
。
这是因为,python的解释器在工作时,会将同一个代码块内重复产生的变量值储存在同一个地址中,下一次出现这个值的时候,就不再重新开辟内存,而是直接引用,而对于代码块外的,则不会共享内存空间。
str型变量
对于str型变量,当字符串长度小于4097时,同值的变量地址相同,大于等于这个长度的时候,即便值相等,物理地址也不相同:
admin@AdmindeiMac ~ python
Python 3.8.8 (default, Apr 13 2021, 12:59:45)
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 'a' * 4096
>>> b = 'a' * 4096
>>> a == b
True
>>> a is b
True
>>> id(a)
140645130248704
>>> id(b)
140645130248704
>>>
>>> a = 'a' * 4097
>>> b = 'a' * 4097
>>> a == b
True
>>> a is b
False
>>> id(a)
140645138213376
>>> id(b)
140645138203136