• 2024 年 12 月 22 日

Python中 is 和 == 的区别

今天在项目中遇到了一个令人费解的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 = 1b = 1时,虽然变量名不同,但实际指向的内存地址是一样的,即id一样,所以is判断的结果返回True

但当a = 4567b = 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了呢?但是ab判断的结果却返回的是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

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注