ChatGPT解决这个技术问题 Extra ChatGPT

Python 2.X 中的 range 和 xrange 函数有什么区别?

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

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

M
MattDMo

在 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 都是根据需要而不是初始化进行评估的。
S
ShadowRanger

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)))。
S
ShadowRanger

请记住,使用 timeit 模块来测试哪个小代码片段更快!

$ 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.77x 就不算多。
S
ShadowRanger

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,但没有最小和最大端点的限制。


B
Ben Hoffstein

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


xrange 返回迭代器。
and only keeps one number in memory at a time,其余的放在哪里,请指导我..
@SIslam 如果它知道开始、结束和当前,它可以计算下一个,一次一个。
S
Sorin

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

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

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

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

图书馆很好,但要得到问题的答案并不总是那么容易。
转到库参考,按 ctrl+f,搜索范围,你会得到两个结果。找到这个问题的答案并不费力。
库参考不起作用。你能更新一下吗?
K
Kishor Pawar

doc 清楚地显示:

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


U
User_Targaryen

在这个简单的示例中,您会发现 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

编码快乐!!


S
ShadowRanger

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

这给您带来了两个好处:

您可以迭代更长的列表而不会出现 MemoryError。由于它懒惰地解析每个数字,如果您提前停止迭代,您将不会浪费时间创建整个列表。


Q
QAZ

这是出于优化的原因。

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

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


k
kvorobiev

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

S
Supercolbat

如果您使用 for 循环,range(x,y) 会返回 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

使用循环时没有太大区别,但仅打印时有区别!


a
abarnert

其他一些答案提到 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 中已修复且未向后移植。


S
Siyaram Malav

在 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__()` 是。
D
Dave Everitt

在循环中针对 xrange 测试范围时(我知道我应该使用 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

我的代码片段测试是否正确?对较慢的 xrange 实例有何评论?或者一个更好的例子:-)


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

python 中的 xrange() 和 range() 的工作方式与 user 类似,但是当我们讨论使用这两个函数时如何分配内存时,区别就来了。

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

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


E
Eddie Deyo

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


h
hacama

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


k
kmario23

什么?
range 在运行时返回一个静态列表。
xrange 返回一个 object(它的作用类似于一个生成器,虽然它肯定不是一个),在需要时从中生成值。

什么时候用哪个?

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

如果要多次迭代列表,请使用范围。

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/…
有趣的问题是当口译员不同意官方文档或其他口译员时该怎么办......但幸运的是,这种情况并不经常出现......
A
ANKUR SATYA

每个人都已经解释得很清楚了。但我想让它亲眼看到。我使用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()”。


R
Rajendra Uppal

来自帮助文档。

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


G
Giorgos Myrianthous

range() 在 Python 中2.x

此函数本质上是 Python 2.x 中可用的旧 range() 函数,并返回包含指定范围内元素的 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'>

S
SomeDoubts

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

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

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


xrange 不创建迭代器实例。它创建一个 xrange 对象,该对象是可迭代的,但不是迭代器——几乎(但不完全是)一个序列,如列表。
u
user299567

Range 返回一个列表,而 xrange 返回一个 xrange 对象,该对象占用相同的内存,而与范围大小无关,因为在这种情况下,每次迭代只生成一个元素并且可用,而在使用范围的情况下,所有元素都会一次生成并且在内存中可用。


E
Evgeni Sergeev

对于 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%。


t
tejaswini teju

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

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


U
U12-Forward

此外,如果做 list(xrange(...)) 将等效于 range(...)

所以 list 很慢。

此外 xrange 确实没有完全完成序列

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


K
Kev

请参阅此 post 以找出 range 和 xrange 之间的区别:

去引用:

range 返回的正是你所想的:一个连续整数的列表,定义的长度从 0 开始。然而,xrange 返回一个“xrange 对象”,它的作用很像迭代器


我意识到这已经 5 岁了,但是那个帖子几乎对所有事情都是错误的。 xrange 不是迭代器。 range 返回的列表确实支持迭代(列表几乎是可迭代的原型示例)。 xrange 的整体好处不是“最小的”。等等。