range() 真的创建列表吗?
我的教授和这个人都说range
会生成一个数值列表。
“注意:range函数只是返回一个包含从x到y-1的数字的列表。例如,range(5, 10)返回的列表是[5, 6, 7, 8, 9]。”
我觉得这个说法不太准确,因为:
type(range(5, 10))
<class 'range'>
而且,使用range
生成的整数,唯一明显的访问方式就是一个一个地遍历,这让我觉得把range
称作列表是不对的。
4 个回答
Python3
使用 range
函数的 for
循环
要理解在 Python3 中 for i in range()
的意思,我们首先需要了解 range()
函数是怎么工作的。
range()
函数用生成器来产生数字。它并不是一次性生成所有数字。
正如你所知道的,range()
返回的是一个 range
对象。这个 range
对象无论表示的范围有多大,都只占用相同(很小的)内存。它只存储起始值、结束值和步长,然后根据需要计算每个数字和子范围。
也就是说,只有当 for
循环需要下一个值时,它才会生成下一个值。在每次循环中,它会生成下一个值,并把它赋值给迭代变量 i
。
所以这意味着 range()
会随着循环的进行一个一个地生成数字。 这样可以节省很多内存,使得 range()
更快、更高效。
在Python 2.x版本中,使用range会创建一个列表。如果你只需要用到一次range,那就没问题;但如果要用多次的话,建议使用xrange,因为它会生成一个生成器,这样可以减少内存的使用,有时候也能节省时间,因为它是懒加载的方式。
在Python 3.x版本中没有xrange,range的功能就相当于Python 2.x中的xrange。
想了解更多,可以参考这个问题: Python 2.X中range和xrange函数有什么区别?
这要看情况。
在 Python 2.x 版本中,range
会生成一个列表(列表也是一种序列),而 xrange
则会生成一个 xrange
对象,这个对象可以用来逐个遍历值。
而在 Python 3.x 版本中,range
生成的是一个可迭代的对象(更具体来说,是一种序列)。
在Python 2.x中,range
会返回一个列表,但在Python 3.x中,range
返回的是一种不可变的序列,类型是range
。
Python 2.x:
>>> type(range(10))
<type 'list'>
>>> type(xrange(10))
<type 'xrange'>
Python 3.x:
>>> type(range(10))
<class 'range'>
在Python 2.x中,如果你想得到一个可迭代的对象,像在Python 3.x中那样,你可以使用xrange
函数,它返回的是一种不可变的序列,类型是xrange
。
xrange
相比于range
在Python 2.x中的优点:
xrange()
比range()
的优势不大(因为xrange()
在请求值时仍然需要创建这些值),除非在内存紧张的机器上使用非常大的范围,或者当范围内的所有元素都不会被使用时(比如循环通常用break结束)。
注意:
此外,访问
range()
创建的整数的唯一明显方法是通过迭代它们。
其实并不是这样。因为Python 3中的range
对象是不可变的序列,它们也支持索引。引用range
函数的文档:
范围实现了所有常见的序列操作,除了连接和重复。
...
范围对象实现了
collections.abc.Sequence
ABC,并提供了包含测试、元素索引查找、切片和支持负索引等功能。
例如,
>>> range(10, 20)[5]
15
>>> range(10, 20)[2:5]
range(12, 15)
>>> list(range(10, 20)[2:5])
[12, 13, 14]
>>> list(range(10, 20, 2))
[10, 12, 14, 16, 18]
>>> 18 in range(10, 20)
True
>>> 100 in range(10, 20)
False
所有这些操作都可以在这个不可变的range
序列上进行。
最近,我遇到了一个问题,我觉得在这里提到它是合适的。考虑这段Python 3.x代码:
from itertools import islice
numbers = range(100)
items = list(islice(numbers, 10))
while items:
items = list(islice(numbers, 10))
print(items)
人们会期望这段代码打印出每十个数字,直到99。但它会无限运行。你能想出原因吗?
解决方案
因为
range
返回的是一个不可变的序列,而不是一个迭代器对象。所以,每当对range
对象使用islice
时,它总是从头开始。可以把它想象成一个不可变列表的替代品。现在问题来了,如何解决这个问题?很简单,你只需要从中获取一个迭代器。只需将
numbers = range(100)
改为
numbers = iter(range(100))
现在,numbers
是一个迭代器对象,它会记住之前迭代了多长时间。所以,当islice
迭代它时,它会从上次结束的地方开始。