Python之禅Python语言与信息数据获取和机器学习Python 运维

为什么执行 x in range(1000000000) 如此

2017-02-16  本文已影响92人  liuzhijun

在 Python 中,表达式 1000000000000000 in range(1000000000000001) 的执行速度能有多快?

判断一个元素 x 是否存在于集合 y 中最简单粗暴地方法就是迭代,每次取出一个值与之比较,如果集合中存在一个值 z 等于 x就返回 true ,它的时间复杂度是 O(n),使用哈希算法的理论时间复杂度是 O(1),二分查找的时间复杂度是 O(log n),那么 Python 究竟会采用的哪种算法来实现呢?

先来做个实验:

#python2

timeit.timeit('1000000000 in range(0,1000000000,10)', number=1)
5.50357640805305

timeit.timeit('1000000000 in xrange(0,1000000000,10)', number=1)
2.3025200839183526

# python3

import timeit
timeit.timeit('1000000000 in range(0,1000000000,10)', number=1)
4.490355838248402e-06

我们都知道 python2 中的 range 函数返回的是一个列表对象,一次性把所有的元素加载到内存,所以执行第一个表达式的时候,系统会突然感觉非常卡顿,它需要的时间是5秒多。

xrange 和 python3 中的 range 函数类似,都是返回一个迭代器对象,但是它俩的执行结果相差悬殊,让人大跌眼镜。第三个表达式所花的时间接近0秒,为何 python2 的 xrange 与 python3 中 range 函数区别这么大?为了弄明白其中的玄机,我们要理解in操作是如何执行的。根据 Python 文档 in 的规则:

明白了 in 的规则之后,我们先看看 xrange 提供了哪些方法:

dir(xrange)

['__class__','__getitem__', '__hash__', '__init__', 
'__iter__', '__len__', '__new__', ...]

是的,xrange 函数只实现了 getitemiter,判断 x 是 是否在 y 中需要逐个值迭代进行比较,也就是说 xrange 的时间复杂度是O(n)。

再来看看 python3 的 range 有哪些方法:

 dir(range)
['__class__', '__contains__', '__getitem__', '__iter__',  
'count', 'index', 'start', 'step', 'stop', ...]

range 提供的属性比 xrange 要多很多,不仅实现了 getitemiter ,还实现了 contains ,所以它会优先调用contains方法,此外,它还提供了三个属性 start、stop、step。那么究竟为什么它的执行速度会如此之快呢?来看看contains方法是如何实现的吧。

在 Python3 中,contains 并不是逐个值迭代对比,而是采用这样一种逻辑:

也可以把它当作一到面试题来问:Python2 中的 xrange 与 python3 中的 range 有什么区别?它不仅可以考察候选者对 Python3 的熟悉程度,而且可以看出候选者对一个知识点的理解深度。

关注公众号『一个程序员的微站』获取最新 Python 干货和有温度的内容


关注关注
上一篇下一篇

猜你喜欢

热点阅读