Python 精讲 | 奇葩的 is

大家好,欢迎来到 Crossin的编程教室 !

接下来的几个例子,可能会颠覆你对 Python 的认知。

我们知道,Python 判断两个数值是否相等的运算符是「==」。比如有一个变量 a 是整数 1,另一个变量 b 是小数 1.0,尽管它们类型不同,但代表的数值是相等的,所以 a == b 结果是 True。

12954aed58008bb94b2d46a983294053.png

Python 中还有一个运算符 is,它用来判断两个对象是否相同。

一个是相等,一个是相同,虽然只差一个字,但 is 却没有那么简单。

我们打开一个 Python 交互环境,在里面定义一个变量 a = 1.0,再定义一个变量 b = a。

a is b 的结果是 True,这个还算好理解,因为 b 就是 a 嘛。

615d90d2eaa0ba1ef0f7443c170ad939.png

如果 b 不是由 a 赋值,而是直接赋值为 1.0。这时 a is b 的结果就是 False。这个也可以理解,虽然值相等,但它们是两个变量,并不相同。

4fda6e98c2b7bd56b57ce3e06de40623.png

不过接下来,情况就开始变得复杂了。

你要说分别赋值的变量就是不相同,那我们把赋给变量的值,从 1.0 改成 1,结果就又成了 True。

c38be045fc374675d4c544e903e58855.png

难道是因为浮点数和整数类型的原因吗?

那我们再把值从 1,改成 1000,同样是整数,结果却是 False。

81ff902e1f4245083e7e5a25d2947d84.png

然而还没完,我们把同样的代码写在一个 py 文件中运行,结果就是 True。

d33db81f663370f5a8d6d3b533a75263.png

但也不全是 True。如果这两个变量不在一个作用域,就是 False。

648a31198cbe8e011199429d5d824728.png

你可能要说,不同作用域的变量肯定不相同嘛,但如果值改回为 1,又成了 True。

a96a01a601d8b2a171c01b6a92953f50.png

前面的例子都是直接赋值,那如果加入计算会怎样?

我们在让 b 在 a 的基础上加上 0,b 的值完全没有变化,结果却从 True 变成了 False。

2b1426a850a019234c3b17df7c91ae0b.png

但再换个计算式,又是 True

608e1166dd002c348831ee7a66cab2b2.png

这到底是怎么回事呢?

背后的原因其实是 Python 解释器的三个优化操作。首先,是

1. 小整数池

Python 为了优化速度,在每次执行代码时,会提前把 -5 到 256 的整数创建好。因为这些小整数是会被经常用到的。而当你创建一个值在这个范围内的整数时,就不是临时再去创建一个对象,而是直接指向已经建好的对象。所以不管你有多少个变量,实际都是同一个对象。

0d920ac8202755d4e060941d9ed0e1a5.png

我们可以用id函数来验证这一点:

181b89f394002853b905b9ac669dc961.png

而对于小数没有这样的优化,因为小数实在太多了。大于 256 的整数也没有。

ea178ecd467fbd6f47e1ff00a3c9af5b.png

那为什么写在 py 文件里的大整数就是相同的呢?这就要说到 Python 另一个优化:

2. 大整数缓存

尽管大于 256 的整数不会提前创建好,但如果 Python 解释器发现你用到重复的整数常量,也会将后面的变量指向已经创建好的对象。

5ef81b36cbbdbb1ad47d9ad79b6ffe4a.png

所以不仅是在 py 文件中,即使在交互环境下,如果把两个大整数的赋值写在同一行,或者放在一个代码块中,也会发现它们是相同的。

fc21dfa17acde6cd2fa24b37d047428c.png

f85e16a5a41d1ef48c8be5c4e83cb89a.png

但这种优化仅限于数值常量,对于带有变量的计算就不起作用了,因为 Python 无法提前预判变量的值。

而对于不带变量的纯数值计算,Python 又做了一次优化:

3. 常量折叠

Python 在编译阶段会把常量表达式计算成结果并替换。所以不管你是 10 * 100 还是 10 ** 3 又或是 111 * 9 + 1,对 Python 来说都是 1000,于是也同样被缓存了。

67ac8ffba4e637f3885e779d89bbeac7.png

以上这些,就是 is 会呈现出看似混乱结果的原因。但请注意,这些解释器的优化,并不是 Python 语言层面的特性。它们可能因环境和版本的不同而产生不同的结果。

比如在 Python 3.7 中,不同作用域的大整数不会被缓存为同一个对象,但在 Python 3.11 中,却是相同的。

26b5022ce9d18604361e0728093deea2.png

fa6bd1f481e11d189b7d225fdb83be5e.png

作为开发者来说,最好的选择就是不要在比较数值相等时使用 is。只有一种情况建议用 is 而不是 ==,那就是判断一个值是否为空值 None。

你知道是什么原因吗?欢迎留言区讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Crossin的编程教室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值