这是一个非常经典的问题,主要涉及到 Python 2 和 Python 3 的区别。

核心结论(一句话总结)
- 在 Python 2 中:
range返回一个列表,而xrange返回一个 xrange 对象(一个轻量级的迭代器)。 - 在 Python 3 中:
range的行为等同于 Python 2 的xrange,返回一个range对象(一个不可变的序列类型)。xrange这个函数已经被完全移除了。
详细解释
Python 2 中的 range 和 xrange
range()
在 Python 2 中,range() 函数会立即生成一个包含所有指定数字的列表,并将其存储在内存中。
工作方式:
range(start, stop, step) 会生成一个从 start 到 stop-1 的列表,步长为 step。
特点:
- 内存消耗大:如果生成一个很大的数字范围,
range(10000000),Python 会在内存中创建一个包含一千万个元素的列表,这会消耗大量内存,甚至可能导致程序崩溃。 - 速度快:因为列表已经生成,所以访问其中的元素(如
my_list[9999999])非常快,时间复杂度是 O(1)。
示例:

# Python 2 r = range(5) print r # 输出: [0, 1, 2, 3, 4] print type(r) # 输出: <type 'list'> # 内存消耗巨大 # huge_list = range(10000000) # 这会创建一个包含1000万个元素的列表
xrange()
为了解决 range() 内存消耗大的问题,Python 2 引入了 xrange()。
工作方式:
xrange() 不会一次性生成所有数字,它返回一个 xrange 对象,这个对象是一个序列类型,但它更像是懒加载(lazy loading)的,它只在需要的时候才计算并生成下一个数字。
特点:
- 内存消耗极小:无论
xrange(10000000)还是xrange(1000000000000),它都只占用固定的少量内存,因为它只存储了start,stop,step这三个参数,而不是实际的数字序列。 - 惰性计算:当你需要遍历它时(例如在
for循环中),它会一个一个地生成数字,你不能像列表一样通过索引直接访问任意位置的元素(在 Python 2.7 中,xrange对象支持索引,但实现上仍然是惰性的,比列表慢)。 - 性能:在
for循环中进行迭代时,xrange的性能通常比range更好,因为它不需要预先分配整个列表的内存。
示例:

# Python 2 xr = xrange(5) print xr # 输出: xrange(5) print type(xr) # 输出: <type 'xrange'> # 内存消耗极小 # huge_xr = xrange(10000000) # 这只创建了一个xrange对象,不占用大量内存 # for i in huge_xr: # 遍历时才逐个生成数字 # print(i) # 尝试直接访问元素(在Python 2.7中是可行的,但底层机制不同) # print xr[3] # 输出: 3
对比总结 (Python 2)
| 特性 | range() |
xrange() |
|---|---|---|
| 返回类型 | list |
xrange 对象(类序列) |
| 内存使用 | 高,一次性生成所有元素 | 低,只存储参数,惰性生成 |
| 适用场景 | 需要多次随机访问列表元素,且列表不大 | 主要用于 for 循环迭代,处理大范围数字 |
| 性能 | 创建时慢,访问快 | 创建时快,迭代时高效 |
Python 3 中的 range
在 Python 3 的设计过程中,开发者们认为 xrange() 的优点(特别是内存效率)是如此重要,以至于它应该成为标准行为,他们做出了以下决定:
- 移除
xrange():xrange这个函数在 Python 3 中不复存在,尝试使用xrange(5)会直接抛出NameError。 - 重用
range():将range()函数的行为改为与 Python 2 的xrange()完全一致。
Python 3 中的 range() 特点:
- 返回
range对象:range()返回的是一个range对象,它是一个不可变的序列类型,你可以把它看作是一个智能的、只读的数字序列。 - 内存高效:和
xrange一样,它也是惰性计算的,只存储start,stop,step,内存占用恒定。 - 支持索引和切片:
range对象支持通过索引来获取元素,也支持切片操作,这比 Python 2 的xrange更加强大。 - 支持
len()函数:可以获取range对象中元素的数量。 - 支持
in操作符:可以高效地检查一个数字是否在range对象内。
示例:
# Python 3 r = range(5) print(r) # 输出: range(0, 5) print(type(r)) # 输出: <type 'range'> # 内存高效 # huge_r = range(10000000) # 内存占用很小 # print(len(huge_r)) # 输出: 10000000 # 支持索引和切片 print(r[3]) # 输出: 3 print(r[1:4]) # 输出: range(1, 4) # 支持 'in' 操作符 print(3 in r) # 输出: True print(10 in r) # 输出: False
为什么 Python 3 要做出这个改变?
- 统一行为:简化了语言,开发者不再需要为“我应该用
range还是xrange”而烦恼,对于绝大多数只需要在循环中迭代数字的场景,range是最佳选择。 - 内存效率优先:在处理大数据、文件、网络流等现代编程任务中,内存效率至关重要,将
range设计为内存高效的模式,使得 Python 在处理大范围数字时更加健壮和强大。 - 功能增强:Python 3 的
range对象不仅内存高效,还支持索引、切片等列表操作,结合了xrange的优点和列表的部分便利性,功能更完善。
现代编程中的最佳实践
-
如果你使用的是 Python 3:忘掉
xrange,只使用range,无论是小循环还是大范围数字,range都是正确且高效的选择。 -
如果你维护的是 Python 2 代码:
- 在
for循环中,优先使用xrange,除非你有非常明确且充分的理由需要立即使用一个列表。 - 如果你确实需要一个包含所有数字的列表(需要多次通过索引访问),那么再使用
range。
- 在
-
兼容性(Python 2/3):如果你需要编写同时兼容 Python 2 和 Python 3 的代码,可以使用一个技巧:
try: # Python 2 xrange(5) except NameError: # Python 3 xrange = range # 现在在两个版本中都可以使用 xrange 了 for i in xrange(5): print(i)随着 Python 2 在 2025 年正式停止支持,这种兼容性代码已经越来越少见。
总结表格
| 版本 | range |
xrange |
|---|---|---|
| Python 2 | 返回 list,内存消耗大 |
返回 xrange 对象,内存高效,惰性计算 |
| Python 3 | 返回 range 对象,内存高效,惰性计算,支持索引/切片 |
已移除 |
