【问题标题】:What is the difference between range and xrange functions in Python 2.X?Python 2.X 中的 range 和 xrange 函数有什么区别?
【发布时间】:2010-09-10 19:56:16
【问题描述】:

显然 xrange 更快,但我不知道为什么它更快(除了传闻之外没有证据表明它更快)或者除此之外还有什么不同

for i in range(0, 20):
for i in xrange(0, 20):

【问题讨论】:

    标签: python loops range python-2.x xrange


    【解决方案1】:

    doc 清楚地写着:

    此函数与range() 非常相似,但返回的是xrange 对象而不是列表。这是一个不透明的序列类型,它产生与相应列表相同的值,但实际上并没有同时存储它们。 xrange() 相对于range() 的优势很小(因为xrange() 在被要求时仍然必须创建值),除非在内存不足的机器上使用非常大的范围或所有范围的元素都从未使用过(例如循环通常以break 终止时)。

    【讨论】:

      【解决方案2】:

      在 Python 2.x 中:

      • range 创建一个列表,因此如果您执行range(1, 10000000),它会在内存中创建一个包含9999999 元素的列表。

      • xrange 是一个惰性求值的序列对象。

      在 Python 3 中:

      • range 相当于 Python 2 的 xrange。要获取列表,您必须明确使用list(range(...))
      • xrange 不再存在。

      【讨论】:

      • 他们为什么要做 xrange,而不是让 range 变得懒惰?
      • @RobertGrant 如果您对该列表进行 1000 次迭代,每次生成值都会变慢
      • @RobertGrant,他们做到了。在 Python 3 中。(他们不能在 Python 2.x 行中做到这一点,因为所有更改都必须向后兼容。)
      • 有人能解释一下“懒惰地评估”是什么意思吗?谢谢!
      • @Ratul 这意味着每个i 都是按需评估而不是初始化。
      【解决方案3】:

      记住,使用timeit 模块来测试哪个小sn-ps 代码更快!

      $ python -m timeit 'for i in range(1000000):' ' pass'
      10 loops, best of 3: 90.5 msec per loop
      $ python -m timeit 'for i in xrange(1000000):' ' pass'
      10 loops, best of 3: 51.1 msec per loop
      

      就个人而言,我总是使用range(),除非我正在处理真的巨大的列表——正如你所看到的,从时间上看,对于一百万个条目的列表,额外的开销是只有 0.04 秒。正如 Corey 指出的那样,在 Python 3.0 中,xrange() 将消失,range() 无论如何都会为您提供良好的迭代器行为。

      【讨论】:

      • +1 用于 timeit 示例。注意:要在windows cmd中运行,需要使用双引号,即“。所以代码将是python -m timeit "for i in xrange(1000000):" " pass"
      • xrange 的主要好处是内存,而不是时间。
      • +1 实际答案:使用范围,除非很大。顺便说一句,它们在概念上是相同的,对吗?奇怪的是没有答案说明这一点。
      • 如果 xrange 更快且不占用内存,为什么还要使用 range?
      • 我一般同意你的说法,但你的评价是错误的:the extra overhead is only 0.04 seconds 不是正确的看待它的方式,(90.5-51.1)/51.1 = 1.771 times slower 是正确的,因为它传达了如果这是你的核心循环编程它可能会成为瓶颈。但是,如果这只是一小部分,那么 1.77 倍也不算多。
      【解决方案4】:

      range 创建一个列表,因此如果您执行range(1, 10000000),它会在内存中创建一个带有9999999 元素的列表。

      xrange 是一个生成器,所以它是一个序列对象是一个,它是惰性求值的。

      这是真的,但在 Python 3 中,range() 将由 Python 2 xrange() 实现。如果您需要实际生成列表,则需要执行以下操作:

      list(range(1,100))
      

      【讨论】:

      • 我不认为这是一个大问题(关于破坏现有应用程序),因为 range 主要用于生成要在 for 循环中使用的索引,如“for i in range(1, 10):”
      • +1 感谢您的回答,有关 Python 3 用 xrange 替换 range 的信息非常有用。我实际上告诉某人使用 xrange 或 range ,他们说这在 python 3 中并不重要,所以我用谷歌搜索了更多信息,这个答案出现了:)
      • @winterlight,认为它的正确术语是迭代器。生成器也应该能够接收。
      • @scign,有关生成器协议的规范定义,请参见 PEP 342。可以在type annotation documentation 中找到一个很好的总结(这些别名为typing.*)。
      • 不,我不知道,@scign。您是否阅读过链接的 PEP 和文档?在过去,这两个术语可能可以互换使用,但在撰写本文时,生成器必须能够接收值。此外,Python 3 range 也不是迭代器(试试next(range(42)))。
      【解决方案5】:

      range() 在 Python 中 2.x

      这个函数本质上是旧的 range() 函数,它在 Python 2.x 中可用,并返回一个包含指定范围内元素的 list 对象的实例。

      但是,在初始化具有一系列数字的列表时,这种实现效率太低了。例如,for i in range(1000000) 在内存和时间使用方面都是一个非常昂贵的命令,因为它需要将此列表存储到内存中。


      range() 在 Python 中 3.xxrange() 在 Python 中 2.x

      Python 3.x 引入了 range() 的更新实现(而更新的实现已经在 Python 2.x 中通过 xrange() 函数提供)。

      range() 利用了一种称为 惰性求值的策略。较新的实现引入了 range 类,它是一个轻量级对象,代表所需的元素,而不是在范围内创建大量元素列表。给定范围内的元素,不将它们显式存储在内存中(这可能听起来像生成器,但惰性求值的概念不同)。


      例如,考虑以下内容:

      # Python 2.x
      >>> a = range(10)
      >>> type(a)
      <type 'list'>
      >>> b = xrange(10)
      >>> type(b)
      <type 'xrange'>
      

      # Python 3.x
      >>> a = range(10)
      >>> type(a)
      <class 'range'>
      

      【讨论】:

        【解决方案6】:

        请花一些时间与Library Reference 联系。您对它越熟悉,就越能更快地找到此类问题的答案。尤其重要的是关于内置对象和类型的前几章。

        xrange 类型的优点是 xrange 对象总是 占用相同数量的内存,无论它代表的范围大小。 没有一致的性能优势。

        另一种快速查找有关 Python 构造信息的方法是文档字符串和帮助函数:

        print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
        help(xrange)
        

        【讨论】:

        • 图书馆很好,但要找到问题的答案并不总是那么容易。
        • 去库参考,按ctrl+f,搜索范围,你会得到两个结果。找到这个问题的答案并不费力。
        • 库参考不起作用。可以更新一下吗?
        【解决方案7】:

        另外,如果做list(xrange(...)) 将等同于range(...)

        所以list 很慢。

        还有xrange真的没有完全完成序列

        所以这就是为什么它不是一个列表,它是一个 xrange 对象

        【讨论】:

          【解决方案8】:

          来自帮助文档。

          Python 2.7.12

          >>> print range.__doc__
          range(stop) -> list of integers
          range(start, stop[, step]) -> list of integers
          
          Return a list containing an arithmetic progression of integers.
          range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
          When step is given, it specifies the increment (or decrement).
          For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
          These are exactly the valid indices for a list of 4 elements.
          
          >>> print xrange.__doc__
          xrange(stop) -> xrange object
          xrange(start, stop[, step]) -> xrange object
          
          Like range(), but instead of returning a list, returns an object that
          generates the numbers in the range on demand.  For looping, this is 
          slightly faster than range() and more memory efficient.
          

          Python 3.5.2

          >>> print(range.__doc__)
          range(stop) -> range object
          range(start, stop[, step]) -> range object
          
          Return an object that produces a sequence of integers from start (inclusive)
          to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
          start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
          These are exactly the valid indices for a list of 4 elements.
          When step is given, it specifies the increment (or decrement).
          
          >>> print(xrange.__doc__)
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
          NameError: name 'xrange' is not defined
          

          差异显而易见。在 Python 2.x 中,range 返回一个列表,xrange 返回一个可迭代的 xrange 对象。

          在 Python 3.x 中,range 变为 Python 2.x 的 xrange,并删除了 xrange

          【讨论】:

            【解决方案9】:

            每个人都解释得很清楚。但我想让它亲眼看到。我使用python3。因此,我打开了资源监视器(在 Windows 中!),首先,首先执行以下命令:

            a=0
            for i in range(1,100000):
                a=a+i
            

            然后检查“使用中”内存的变化。这是微不足道的。 然后,我运行了以下代码:

            for i in list(range(1,100000)):
                a=a+i
            

            它立即占用了大量内存。而且,我被说服了。 你可以自己试试。

            如果您使用的是 Python 2X,则将第一个代码中的“range()”替换为“xrange()”,将“list(range())”替换为“range()”。

            【讨论】:

              【解决方案10】:

              range :-range 将一次填充所有内容。这意味着范围的每个数字都将占用内存。

              xrange :-xrange 类似于生成器,当您想要数字范围但又不想存储它们时,它就会出现,例如当您想在 for 循环中使用时。所以内存效率很高。

              【讨论】:

                【解决方案11】:

                range(x,y) 如果使用for 循环,则返回 x 和 y 之间的每个数字的列表,然后range 会更慢。事实上,range 的索引范围更大。 range(x.y) 将打印出 x 和 y 之间所有数字的列表

                xrange(x,y) 返回xrange(x,y),但如果您使用for 循环,则xrange 更快。 xrange 的索引范围较小。 xrange 不仅会打印出xrange(x,y),还会保留其中的所有数字。

                [In] range(1,10)
                [Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
                [In] xrange(1,10)
                [Out] xrange(1,10)
                

                如果你使用for 循环,那么它会起作用

                [In] for i in range(1,10):
                        print i
                [Out] 1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                [In] for i in xrange(1,10):
                         print i
                [Out] 1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                

                使用循环并没有太大区别,只是打印时有区别!

                【讨论】:

                  【解决方案12】:

                  在这个简单的示例中,您会发现xrange 相对于range 的优势:

                  import timeit
                  
                  t1 = timeit.default_timer()
                  a = 0
                  for i in xrange(1, 100000000):
                      pass
                  t2 = timeit.default_timer()
                  
                  print "time taken: ", (t2-t1)  # 4.49153590202 seconds
                  
                  t1 = timeit.default_timer()
                  a = 0
                  for i in range(1, 100000000):
                      pass
                  t2 = timeit.default_timer()
                  
                  print "time taken: ", (t2-t1)  # 7.04547905922 seconds
                  

                  xrange 的情况下,上面的例子并没有更好地反映任何事情。

                  现在看看下面的例子,与xrange相比,range 真的很慢。

                  import timeit
                  
                  t1 = timeit.default_timer()
                  a = 0
                  for i in xrange(1, 100000000):
                      if i == 10000:
                          break
                  t2 = timeit.default_timer()
                  
                  print "time taken: ", (t2-t1)  # 0.000764846801758 seconds
                  
                  t1 = timeit.default_timer()
                  a = 0
                  for i in range(1, 100000000):
                      if i == 10000:
                          break
                  t2 = timeit.default_timer() 
                  
                  print "time taken: ", (t2-t1)  # 2.78506207466 seconds
                  

                  使用range,它已经创建了一个从 0 到 100000000(耗时)的列表,但 xrange 是一个生成器,它只根据需要生成数字,即如果迭代继续进行。

                  在 Python-3 中,range 功能的实现与 Python-2 中的 xrange 相同,而在 Python-3 中他们取消了 xrange

                  编码愉快!!

                  【讨论】:

                    【解决方案13】:

                    range(): range(1, 10) 返回一个包含 1 到 10 个数字的列表并将整个列表保存在内存中。

                    xrange(): 与 range() 类似,但不是返回列表,而是返回一个对象,该对象根据需要生成范围内的数字。对于循环,这比 range() 稍微快一点,并且内存效率更高。 xrange() 对象就像一个迭代器,并按需生成数字。(懒惰评估)

                    In [1]: range(1,10)
                    
                    Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
                    
                    In [2]: xrange(10)
                    
                    Out[2]: xrange(10)
                    
                    In [3]: print xrange.__doc__
                    
                    xrange([start,] stop[, step]) -> xrange object
                    

                    【讨论】:

                      【解决方案14】:

                      在 python 2.x 中

                      range(x) 返回一个列表,该列表是在内存中使用 x 个元素创建的。

                      >>> a = range(5)
                      >>> a
                      [0, 1, 2, 3, 4]
                      

                      xrange(x) 返回一个 xrange 对象,它是一个生成器 obj,它根据需要生成数字。它们是在 for 循环期间计算的(惰性求值)。

                      对于循环,这比 range() 稍微快一点,而且内存效率更高。

                      >>> b = xrange(5)
                      >>> b
                      xrange(5)
                      

                      【讨论】:

                      • xrange() 不是生成器。 xrange(n).__iter__()` 是。
                      【解决方案15】:

                      python 中的 xrange() 和 range() 与 user 的工作方式类似,但不同之处在于我们讨论使用这两个函数时如何分配内存。

                      当我们使用 range() 时,我们为它生成的所有变量分配内存,因此不建议使用较大的 no。要生成的变量。

                      另一方面,xrange() 一次只生成一个特定的值,并且只能与 for 循环一起使用以打印所需的所有值。

                      【讨论】:

                        【解决方案16】:

                        range 创建一个列表,因此如果您执行 range(1, 10000000) 它会在内存中创建一个包含 10000000 个元素的列表。 xrange 是一个生成器,所以它的计算是惰性的。

                        这会给你带来两个好处:

                        1. 您可以迭代更长的列表而无需获得MemoryError
                        2. 由于它会延迟解析每个数字,因此如果您提前停止迭代,就不会浪费时间创建整个列表。

                        【讨论】:

                          【解决方案17】:

                          xrange 仅存储范围参数并按需生成数字。然而,Python 的 C 实现目前将其 args 限制为 C long:

                          xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
                          range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]
                          

                          请注意,在 Python 3.0 中只有 range,它的行为类似于 2.x xrange,但没有最小和最大端点的限制。

                          【讨论】:

                            【解决方案18】:

                            对于range(..) / xrange(..) 的较小参数,差异会减小:

                            $ python -m timeit "for i in xrange(10111):" " for k in range(100):" "  pass"
                            10 loops, best of 3: 59.4 msec per loop
                            
                            $ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" "  pass"
                            10 loops, best of 3: 46.9 msec per loop
                            

                            在这种情况下,xrange(100) 的效率仅提高了大约 20%。

                            【讨论】:

                              【解决方案19】:

                              什么?
                              range 在运行时返回一个静态列表。
                              xrange 返回一个object(它的作用就像一个生成器,虽然它肯定不是一个),在什么时候生成值必填。

                              什么时候用哪个?

                              • 如果您想为一个巨大的范围(比如 10 亿)生成一个列表,请使用 xrange,尤其是当您拥有像手机这样的“内存敏感系统”时。
                              • 如果您想多次迭代列表,请使用range

                              PS:Python 3.x 的 range 函数 == Python 2.x 的 xrange 函数。

                              【讨论】:

                              • xrange 不返回生成器对象。
                              • 如果我理解正确,这就是这里的解释(对于 Python 2.x):wiki.python.org/moin/Generators
                              • 那么wiki就错了。 (我不知道添加并签署该评论的“SH”是谁。)The official documentation 是对的;你可以自己测试一下,看看是生成器还是序列。
                              • 好的。但是读完后仍然感到困惑:stackoverflow.com/questions/135041/…
                              • 有趣的问题是当解释器不同意官方文档或不同的解释器时该怎么办......但幸运的是,这种情况并不经常出现......
                              【解决方案20】:

                              其他一些答案提到 Python 3 消除了 2.x 的 range 并将 2.x 的 xrange 重命名为 range。但是,除非您使用的是 3.0 或 3.1(没有人应该使用),否则它实际上是一种不同的类型。

                              正如the 3.1 docs 所说:

                              Range 对象的行为很少:它们只支持索引、迭代和len 函数。

                              然而,在 3.2+ 中,range 是一个完整的序列——它支持扩展切片,以及 collections.abc.Sequence 的所有方法与 list 具有相同的语义。*

                              而且,至少在 CPython 和 PyPy(目前仅有的两个 3.2+ 实现)中,它还具有 indexcount 方法和 in 运算符的恒定时间实现(只要你只传递整数)。这意味着在 3.2+ 中写 123456 in r 是合理的,而在 2.7 或 3.1 中这将是一个可怕的想法。


                              * issubclass(xrange, collections.Sequence) 在 2.6-2.7 和 3.0-3.1 中返回 True 的事实是 a bug 在 3.2 中已修复且未向后移植。

                              【讨论】:

                                【解决方案21】:

                                Range 返回一个 listxrange 返回一个 xrange 对象,无论范围如何,它都占用相同的内存size,在这种情况下,每次迭代只生成一个元素并且可用,而在使用范围的情况下,所有元素都是一次生成并在内存中可用。

                                【讨论】:

                                  【解决方案22】:

                                  关于扫描/打印 0-N 个项目的需求,range 和 xrange 的工作原理如下。

                                  range() - 在内存中创建一个新列表并获取整个 0 到 N 项(总共 N+1)并打印它们。 xrange() - 创建一个迭代器实例,它扫描项目并仅将当前遇到的项目保留在内存中,因此始终使用相同数量的内存。

                                  如果所需元素位于列表的开头,那么它可以节省大量时间和内存。

                                  【讨论】:

                                  • xrange 不创建迭代器实例。它创建了一个xrange 对象,它是可迭代的,但不是迭代器——几乎(但不完全是)一个序列,就像一个列表。
                                  【解决方案23】:

                                  查看此post 以查找 range 和 xrange 之间的区别:

                                  引用:

                                  range 完全按照您的想法返回:连续的列表 整数,具有从 0 开始的定义长度。xrange,但是, 返回一个“xrange 对象”,它的作用很像一个迭代器

                                  【讨论】:

                                  • 我意识到这已经 5 岁了,但那篇文章几乎所有内容都是错误的。 xrange 不是迭代器。 range 返回的列表确实支持迭代(列表几乎是可迭代的原型示例)。 xrange 的整体好处不是“最小的”。以此类推。
                                  【解决方案24】:

                                  在循环中针对 xrange 测试 range 时(我知道我应该使用 timeit,但这是使用简单的列表理解示例从内存中迅速破解的)我发现以下内容:

                                  import time
                                  
                                  for x in range(1, 10):
                                  
                                      t = time.time()
                                      [v*10 for v in range(1, 10000)]
                                      print "range:  %.4f" % ((time.time()-t)*100)
                                  
                                      t = time.time()
                                      [v*10 for v in xrange(1, 10000)]
                                      print "xrange: %.4f" % ((time.time()-t)*100)
                                  

                                  给出:

                                  $python range_tests.py
                                  range:  0.4273
                                  xrange: 0.3733
                                  range:  0.3881
                                  xrange: 0.3507
                                  range:  0.3712
                                  xrange: 0.3565
                                  range:  0.4031
                                  xrange: 0.3558
                                  range:  0.3714
                                  xrange: 0.3520
                                  range:  0.3834
                                  xrange: 0.3546
                                  range:  0.3717
                                  xrange: 0.3511
                                  range:  0.3745
                                  xrange: 0.3523
                                  range:  0.3858
                                  xrange: 0.3997 <- garbage collection?
                                  

                                  或者,在 for 循环中使用 xrange:

                                  range:  0.4172
                                  xrange: 0.3701
                                  range:  0.3840
                                  xrange: 0.3547
                                  range:  0.3830
                                  xrange: 0.3862 <- garbage collection?
                                  range:  0.4019
                                  xrange: 0.3532
                                  range:  0.3738
                                  xrange: 0.3726
                                  range:  0.3762
                                  xrange: 0.3533
                                  range:  0.3710
                                  xrange: 0.3509
                                  range:  0.3738
                                  xrange: 0.3512
                                  range:  0.3703
                                  xrange: 0.3509
                                  

                                  我的 sn-p 测试是否正确? xrange 较慢的实例上有任何 cmets 吗?或者一个更好的例子:-)

                                  【讨论】:

                                  • 一次运行这样的基准测试并不能提供准确的计时结果。总是存在差异。它可能是 GC,也可能是另一个进程窃取 CPU ......任何东西。这就是为什么基准测试通常运行 10-100-1000-...
                                  • 这只是一个仓促的 sn-p 打印输出 - 我运行了几次,但最多只有 100 次左右,而xrange 似乎稍微快一些,尽管现在使用 Python 3 进行比较是多余的。
                                  • 这就是timeit 的用途。它负责运行多次、禁用 GC、使用最佳时钟而不是 time 等。
                                  【解决方案25】:

                                  这是为了优化。

                                  range() 将创建一个从头到尾的值列表(在您的示例中为 0 .. 20)。这将成为非常大范围的昂贵操作。

                                  另一方面,

                                  xrange() 更加优化。它只会在需要时(通过 xrange 序列对象)计算下一个值,并且不会像 range() 那样创建所有值的列表。

                                  【讨论】:

                                    【解决方案26】:

                                    xrange 使用迭代器(动态生成值),range 返回一个列表。

                                    【讨论】:

                                      【解决方案27】:

                                      range 生成整个列表并返回它。 xrange 不会——它会按需生成列表中的数字。

                                      【讨论】:

                                        【解决方案28】:

                                        xrange 返回一个迭代器,并且一次只在内存中保留一个数字。 range 将整个数字列表保存在内存中。

                                        【讨论】:

                                        • xrange not 返回一个迭代器。
                                        • and only keeps one number in memory at a time 其余的放在哪里请指导我..
                                        • @SIslam 如果知道开始、结束和当前,它可以计算下一个,一次一个。
                                        猜你喜欢
                                        • 2018-02-18
                                        • 2019-01-23
                                        • 1970-01-01
                                        • 2012-02-20
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 1970-01-01
                                        相关资源
                                        最近更新 更多