`range(n, -1, -1)` 和 `reversed(range(0, n+1))` 之间有啥区别吗?

Posted

技术标签:

【中文标题】`range(n, -1, -1)` 和 `reversed(range(0, n+1))` 之间有啥区别吗?【英文标题】:Is there any difference between `range(n, -1, -1)` and `reversed(range(0, n+1))`?`range(n, -1, -1)` 和 `reversed(range(0, n+1))` 之间有什么区别吗? 【发布时间】:2015-04-12 18:15:22 【问题描述】:

它们产生相同的结果。

>>> for i in range(10, -1, -1):
...     print(i)
... 
10
9
8
7
6
5
4
3
2
1
0

对比:

>>> for i in reversed(range(0, 10 + 1):
...     print(i)
... 
10
9
8
7
6
5
4
3
2
1
0

据我了解,Python3 的range 创建了一个生成器,而不是将整个范围存储在内存中。 reversed 同样一次生成一个值,我想。有什么理由使用其中一个吗?

【问题讨论】:

我猜range() 会比reverse() 更受欢迎,因为后者会导致更多的开销,但正确的分析只能通过记录两者的执行时间来完成 我一直认为反转不会生成生成器,但实际上会生成一个列表。根据 Python 3 文档,它是一个迭代器,但我怀疑它是一个生成器。 docs.python.org/3/library/… 大多数时候,选择实际上是在range(n-1, -1, -1)reversed(range(n)) 之间。第二个选项是可读性的明显赢家,而且它更不容易出错。鉴于时间差异很小,并且循环体几乎总是占主导地位,我几乎总是选择reversed @DovGrobgeld 这是错误的。 Python 有几种方法可以让reversed 避免将整个序列缓存在内存中。如果对象具有__reversed__ 方法,则reversed 委托给该函数。或者,如果对象同时具有__len____getitem__ 方法,则从这些方法生成智能反向迭代器。只有当这两个选项都不可用时,reversed 才会将整个序列缓存在内存中。 @Dunes:实际上,它永远不会依赖于此。尝试reversed(reversed(range(10))),您会发现,外部的reversed 调用并没有尝试存储所有输出来反转它,而是抛出了一个错误。 【参考方案1】:

Python 中的reversed() 函数有一个特殊情况,当您将range() 传递给它时。 reversed(range(...))range(...) 之间唯一真正的区别是您可以多次迭代 range(),但 reversed() 返回一个迭代器,因此它只能使用一次。

>>> iter(range(0, 10))
<range_iterator object at 0x7f735f5badb0>
>>> reversed(range(0, 10))
<range_iterator object at 0x7f735f5baf30>

可以看到,在这两种情况下,迭代器类型都是range_iterator。因此,循环本身的性能在两种情况下都是相同的。

由于reversed() 的唯一开销是一个额外的函数调用,我总是更喜欢reversed(range(10)) 而不是range(9, -1, -1)

【讨论】:

【参考方案2】:

这是在两个模块上使用timeit 的结果

bhargav@bhargav:~$ python -m timeit "for i in range(10, -1, -1):(i)"
1000000 loops, best of 3: 0.466 usec per loop
bhargav@bhargav:~$ python -m timeit "for i in reversed(range(0, 10 + 1)):(i)"
1000000 loops, best of 3: 0.531 usec per loop

如您所见,第二种方式较慢,因为它额外调用了函数reversed

【讨论】:

“这里还注意到生成器比迭代器快 3 倍。” - 这根本不是链接所说的。另外,range 不生成生成器,生成器是一种迭代器。 @user2357112 注意到了!我昨天才读到答案。也许我误解了!我很乐意删除误导性的话。再次感谢 我想我最好把我删除的两个链接留在这里。 1.Speed comparison between iterator and Generator 2.General Difference between Iterator and Generator【参考方案3】:

reversed 使它成为一个迭代器,因此取决于你如何使用它会有不同:

In [1]: r =  reversed(range(0, 10 + 1))

In [2]: next(r)
Out[2]: 10

In [3]: r = range(10, -1, -1)

In [4]: next(r)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-0b5056469c9c> in <module>()
----> 1 next(r)

TypeError: 'range' object is not an iterator

【讨论】:

【参考方案4】:

range(-n+1, -1, -1)reversed(range(n)) 之间唯一真正的区别是range() 返回一个范围对象,在对其进行迭代之前可以进一步使用/操作。而reversed() 返回一个迭代器——你所能做的就是迭代它。

使用范围对象的示例,无法使用迭代器完成。

rng = range(20, 40, 2)
length = len(rng)
element = rng[0]
index_of_element = rng.index(element)
membership_test = 0 in rng
new_rng_from_slice = rng[2:5]

由于范围对象可以被切片,这也开辟了以另一种方式反转它们的可能性:

assert range(n)[::-1] == range(-n+1, -1, -1)

但是,除非需要范围对象的附加功能,否则reversed(range(n)) 更可取,因为它更易于理解。

【讨论】:

以上是关于`range(n, -1, -1)` 和 `reversed(range(0, n+1))` 之间有啥区别吗?的主要内容,如果未能解决你的问题,请参考以下文章

Splay

range()运用及杨辉三角生成器

多项式模板整理

Palindrome C ++程序

对于多维范围,是不是有 Python 的 range(n) 等价物?

Python break和continue的区别 和range的使用