杰瑞科技汇

Python的range和xrange有何区别?

这是一个非常经典的问题,因为它触及了 Python 2 和 Python 3 之间一个重要的区别。

Python的range和xrange有何区别?-图1
(图片来源网络,侵删)

  • Python 2: 同时拥有 range()xrange()
    • range(): 返回一个完整的 列表,所有数字都存储在内存中。
    • xrange(): 返回一个 xrange 对象,这是一个“序列类型”,它只在需要时才生成数字,非常节省内存。
  • Python 3: 只有 range()
    • Python 3 的 range() 行为完全等同于 Python 2 的 xrange(),它返回的是一个 range 对象,而不是列表,同样是惰性计算的,非常节省内存。
    • 如果你需要在 Python 3 中获取一个列表,可以使用 list(range()) 来转换。

xrange 是 Python 2 的一个内存优化版本,而 Python 3 直接将这个优化版本作为了标准的 range 函数。


详细对比

Python 2 中的 range() vs xrange()

在 Python 2 中,这是一个重要的选择,尤其是在处理大范围数字时。

range()

  • 类型: 返回一个 list (列表)。
  • 工作方式: 立即生成,当你调用 range(1000000) 时,Python 会立刻在内存中创建一个包含 1, 0, 1, 2, ..., 999,999 这 100 万个数字的列表。
  • 内存消耗: ,列表的大小与 range 的大小成正比,对于非常大的数字(如 range(100000000)),会消耗大量内存,甚至可能导致程序崩溃。
  • 速度: 访问速度很快,因为数据已经全部加载到内存中。

示例 (Python 2):

# 创建一个包含 0 到 9 的列表
r = range(10)
print type(r)  # <type 'list'>
print r        # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print r[5]     # 5

xrange()

  • 类型: 返回一个 xrange 对象,它不是一个列表,而是一个实现了序列协议的对象。
  • 工作方式: 惰性计算xrange 对象不会一次性生成所有数字,它只记住 start, stop, 和 step 的值,当你需要访问某个数字(例如通过索引 x[5])或迭代它时,它才会根据规则计算出那个特定的数字。
  • 内存消耗: 非常低,无论 range 有多大,xrange 对象本身只占用固定的少量内存,因为它只存储了几个参数。
  • 速度: 迭代时速度和 range 差不多,但创建对象的速度非常快,因为它不需要分配大量内存。

示例 (Python 2):

Python的range和xrange有何区别?-图2
(图片来源网络,侵删)
# 创建一个 xrange 对象
xr = xrange(10)
print type(xr)  # <type 'xrange'>
print xr        # xrange(10)
# print xr[5]   # 这也是可以的,xrange支持索引,它会实时计算
print list(xr)  # 如果强制转换为列表,才会生成所有数字: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Python 2 中的选择建议:

  • 如果只是想在一个 for 循环中迭代,或者只需要访问序列中的个别元素,始终使用 xrange(),这是更高效、更安全的选择。
  • 如果你确实需要一个包含所有数字的列表(用于多次索引访问、切片或传递给一个只接受列表的函数),那么才使用 range()

Python 3 中的 range()

Python 3 的设计者认为 xrange() 的行为更符合大多数实际场景的需求,于是做出了统一。

  • 类型: 返回一个 range 对象,这个对象的行为和 Python 2 的 xrange 几乎完全一样。
  • 工作方式: 惰性计算,和 xrange 一样,它只在需要时才生成数字。
  • 内存消耗: 非常低,这是 range 在 Python 3 中的主要优点。
  • 特点: range 对象是不可变的,你不能修改它里面的元素。

示例 (Python 3):

# 创建一个 range 对象
r = range(10)
print(type(r))   # <class 'range'>
print(r)        # range(0, 10)
# print(r[5])    # 可以索引访问,输出 5
# for i in r:    # 可以迭代
#     print(i)
# 如果你需要一个列表
r_list = list(r)
print(r_list)   # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(type(r_list)) # <class 'list'>

Python 3 中的优势: 因为 range 是惰性的,所以你可以用它来表示非常大的数字范围,而不会耗尽内存。

Python的range和xrange有何区别?-图3
(图片来源网络,侵删)
# 在 Python 3 中,这是完全可行的,只消耗极少量内存
# 一个包含 0 到 999,999,999 的数字序列的“表示”
huge_range = range(1000000000)
# 只有在真正需要时,内存才会被消耗
# 当你尝试把它转换成列表时,程序可能会因为内存不足而崩溃
# huge_list = list(huge_range)  # 警告:这会耗尽你所有的内存!
# 但迭代它,或者取一个元素,是没问题的
print(huge_range[500000000]) # 500000000

总结表格

特性 Python 2 range() Python 2 xrange() Python 3 range()
返回类型 list xrange 对象 range 对象
内存使用 高,立即分配所有数字的内存 低,只存储参数,惰性计算 低,只存储参数,惰性计算
Python 3 中的等价物 (无) Python 3 range() (自身)
主要用途 需要一个真实的列表时 迭代、for 循环、索引访问 迭代、for 循环、索引访问
性能 创建时慢,访问快 创建快,迭代/访问高效 创建快,迭代/访问高效

现代编程建议

  1. 如果你使用的是 Python 3

    • 忘掉 xrange,它不存在了。
    • 在所有需要生成数字序列进行循环或索引的地方,放心大胆地使用 range()
    • 只有当你明确需要一个列表时,才使用 list(range(...))
  2. 如果你在维护 Python 2 代码,或需要编写兼容 Python 2 和 3 的代码

    • 为了内存效率和代码的健壮性,优先使用 xrange()

    • 一个常见的兼容性写法是:

      try:
          # Python 2
          range = xrange
      except NameError:
          # Python 3
          pass
      # 现在在代码中直接使用 range() 即可
      # 在 Python 2 中它会指向 xrange,在 Python 3 中它就是 range
      for i in range(1000000):
          pass

      这种写法让代码在两个版本中都能高效地运行。

分享:
扫描分享到社交APP
上一篇
下一篇