前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python那些年:我们踩过的那些坑(1)

Python那些年:我们踩过的那些坑(1)

作者头像
double
发布2019-07-30 15:40:36
5000
发布2019-07-30 15:40:36
举报
文章被收录于专栏:算法channel算法channel

引用自Python-100-Days

编辑:zglg

1 整数比较入坑

在 Python 中一切都是对象,整数也是对象,在比较两个整数时有两个运算符==is,它们的区别是:

  • is比较的是两个整数对象的id值是否相等,也就是比较两个引用是否代表了内存中同一个地址。
  • ==比较的是两个整数对象的内容是否相等,使用==时其实是调用了对象的__eq__()方法。

知道了is==的区别之后,我们可以来看看下面的代码,了解Python中整数比较有哪些坑:

代码语言:javascript
复制
def main():
	x = y = -1
	while True:
		x += 1
		y += 1
		if x is y:
			print('%d is %d' % (x, y))
		else:
			print('Attention! %d is not %d' % (x, y))
			break
			
	x = y = 0
	while True:
		x -= 1
		y -= 1
		if x is y:
			print('%d is %d' % (x, y))
		else:
			print('Attention! %d is not %d' % (x, y))
			break


if __name__ == '__main__':
	main()

上面代码的部分运行结果如下图所示,出现这个结果的原因是Python出于对性能的考虑所做的一项优化。

对于整数对象,Python把一些频繁使用的整数对象缓存起来,保存到一个叫small_ints的链表中,在Python的整个生命周期内,任何需要引用这些整数对象的地方,都不再重新创建新的对象,而是直接引用缓存中的对象。

Python把频繁使用的整数对象的值定在[-5, 256]这个区间,如果需要这个范围的整数,就直接从small_ints中获取引用而不是临时创建新的对象。因为大于256或小于-5的整数不在该范围之内,所以就算两个整数的值是一样,但它们是不同的对象。

当然仅仅如此这个坑就不值一提了,如果你理解了上面的规则,我们就再看看下面的代码。

2 整数比较第二坑

代码语言:javascript
复制
import dis
a = 257


def main():
	b = 257  # 第6行
	c = 257  # 第7行
	print(b is c)  # True
	print(a is b)  # False
	print(a is c)  # False


if __name__ == "__main__":
	main()

程序的执行结果已经用注释写在代码上了。够坑吧!看上去abc的值都是一样的,但是is运算的结果却不一样。

3 破坑之旅

为什么会出现这样的结果,首先我们来说说Python程序中的代码块。所谓代码块是程序的一个最小的基本执行单位,一个模块文件、一个函数体、一个类、交互式命令中的单行代码都叫做一个代码块。

上面的代码由两个代码块构成,a = 257是一个代码块,main函数是另外一个代码块。

Python内部为了进一步提高性能,凡是在一个代码块中创建的整数对象,如果值不在small_ints缓存范围之内,但在同一个代码块中已经存在一个值与其相同的整数对象了,那么就直接引用该对象,否则创建一个新的对象出来,这条规则对不在small_ints范围的负数并不适用,对负数值浮点数也不适用,但对非负浮点数和字符串都是适用的,这一点读者可以自行证明。

所以 b is c返回了True,而ab不在同一个代码块中,虽然值都是257,但却是两个不同的对象,is运算的结果自然是False了。

为了验证刚刚的结论,我们可以借用dis模块(听名字就知道是进行反汇编的模块)从字节码的角度来看看这段代码。如果不理解什么是字节码,可以先看看《谈谈 Python 程序的运行原理》这篇文章。可以先用import dis导入dis模块并按照如下所示的方式修改代码。

代码语言:javascript
复制
if __name__ == "__main__":
	main()
	dis.dis(main)

代码的执行结果如下图所示。可以看出代码第6行和第7行,也就是main函数中的257是从同一个位置加载的,因此是同一个对象;而代码第9行的a明显是从不同的地方加载的,因此引用的是不同的对象。

如果还想对这个问题进行进一步深挖,推荐大家阅读《Python整数对象实现原理》这篇文章。


推荐阅读

盘点互联网公司最常见的面试编程题

深入浅出Kafka(3):我的生命是如何运转的?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员郭震zhenguo 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档