256 lines
13 KiB
Markdown
256 lines
13 KiB
Markdown
# Python range()函数:示例操作指南
|
||
|
||
> 原文:[https://python.land/deep-dives/python-range](https://python.land/deep-dives/python-range)
|
||
|
||
Python range()函数可用于创建数字序列。range()函数可以被[迭代](https://python.land/deep-dives/python-iterator),并且在与 for 循环结合时是理想的。本文将仔细研究 Python 范围函数:
|
||
|
||
* 这是什么?
|
||
* 如何使用 range()
|
||
* 如何用 range()函数创建 for 循环
|
||
|
||
我还将向您展示如何在 for 循环中使用范围来创建您想要的任何类型的循环,包括:
|
||
|
||
* 数量范围不断增加的常规循环
|
||
* 向后循环(例如从 10 到 0)
|
||
* 跳过值的循环
|
||
|
||
目录
|
||
|
||
|
||
|
||
* [什么是 Python range 函数?](#What_is_the_Python_range_function "What is the Python range function?")
|
||
* [如何使用 Python 范围](#How_to_use_Python_range "How to use Python range")
|
||
* [Python 范围如何工作](#How_a_Python_range_works "How a Python range works")
|
||
* [Python 范围 vs 列表](#Python_range_vs_list "Python range vs list")
|
||
* [实践中的 Python 范围](#Python_Ranges_in_practice "Python Ranges in practice")
|
||
* [结论](#Conclusion "Conclusion")
|
||
|
||
|
||
|
||
## 什么是 Python range 函数?
|
||
|
||
Python 的`range`作为内置函数,通常用于在 [for-loops](https://python.land/introduction-to-python/python-for-loop) 中循环特定次数。像 Python 中的许多东西一样,它实际上是一个 Python 类型(或类),但当在循环中使用它时,我们可以将其视为一个返回 iterable 对象的内置函数。
|
||
|
||
Range 根据我们的输入为我们提供了一个经过计算的数字序列。有三种方法可以调用 range 函数:
|
||
|
||
```py
|
||
# With one integer argument it
|
||
# counts from 0 to stop
|
||
range(stop)
|
||
|
||
# With two integer arguments it
|
||
# counts from start to stop
|
||
range(start, stop)
|
||
|
||
# With three integer arguments it
|
||
# counts from start to stop,
|
||
# with a defined step size
|
||
range(start, stop, step)
|
||
```
|
||
|
||
参数 start、stop 和 step 总是整数。当我们调用 range 时,它返回一个[类](https://python.land/objects-and-classes)的对象`range`。这个 range 对象可以反过来提供一个 [Python 迭代器](https://python.land/deep-dives/python-iterator),这意味着我们可以循环(迭代)它的值。范围迭代器在每次调用时都会计算一个新值。该值基于开始、停止和步长值。
|
||
|
||
如果这听起来很难:的确很难。如果您不完全理解迭代器和可迭代性,此时您有两个选择:
|
||
|
||
1. 继续阅读,看看*如何*使用范围。我不会怪你。
|
||
2. 首先阅读[迭代器和可迭代对象](https://python.land/deep-dives/python-iterator),然后回到这里。
|
||
|
||
如果你想学习 Python,我建议你帮自己一个忙,选择第二个选项。
|
||
|
||
## 如何使用 Python 范围
|
||
|
||
使用范围有三种方式。我们将从最简单的用例开始,详细研究这三个方面。为了让您彻底理解 Python range 函数,稍后我将向您展示 range 在内部是如何工作的;其实挺简单的!然而,最好先看看如何使用范围的例子,这样你就能更好地了解它们是什么以及它们能做什么。
|
||
|
||
### 只有停止值的范围
|
||
|
||
如果我们用一个参数调用`range`,这个参数就是停止值。在这种情况下,`range`将从 0 开始计数,直到达到停止值。
|
||
|
||
例如,如果我们调用`range(5)`,当我们迭代它时,创建的 range 对象将返回值 0、1、2、3 和 4。所以,重要注意: **range 一到达止损值就停止,不会返回止损值本身。**
|
||
|
||
如果这听起来令人困惑,一个简单的例子将有望澄清它。让我们在 for 循环中使用`range`调用:
|
||
|
||
[https://crumb . sh/embed/3ksrw 4 kt ky](https://crumb.sh/embed/3KSrw4Ktuky)
|
||
|
||
#### 为什么 range 不包括结束值?
|
||
|
||
你可能想知道为什么`range`不包括结束值。这一切都归结到一个重要的原则,称为*零基索引*。计算机从零开始计数,而人类从 1 开始计数。当计算机需要给 3 个元素编号时,它从元素 0 开始,然后是 1,然后是 2。索引也是一样。当访问 [Python 列表](https://python.land/python-data-types/python-list)中的元素时,第一个元素驻留在位置 0。我们可以这样访问它:`my_list[0]`。
|
||
|
||
由于这种从零开始的索引,如果 Python 范围也从零开始计数就容易多了。例如,如果你想使用一个范围来遍历一些东西,你可能会想从元素 0 开始。
|
||
|
||
### 带起始值和终止值的范围
|
||
|
||
我们并不总是希望从零开始计数,这就是为什么我们也可以用起始值和终止值来调用 range。对 range(2,5)的调用将返回值 2、3 和 4。这是另一个例子:
|
||
|
||
[https://crumb.sh/embed/ukfdzkeqogo](https://crumb.sh/embed/ukfdzkeqogo)
|
||
|
||
### 步长为正值的范围
|
||
|
||
最后,`range`()采用一个可选参数,称为步长。步长定义了计算出的数字之间的步长。最好只说明这一点:
|
||
|
||
```py
|
||
for i in range(0, 6, 2):
|
||
print(i, end=" ")
|
||
|
||
# Output will be: 0 2 4
|
||
```
|
||
|
||
每一步跳过一个数字,因为步长是 2。让我们增加步长:
|
||
|
||
```py
|
||
for i in range(0, 101, 10):
|
||
print(i, end=" ")
|
||
|
||
# Output will be:
|
||
# 0 10 20 30 40 50 60 70 80 90 100
|
||
```
|
||
|
||
因为我们将`stop`设置为 101,所以值 100 也包含在这个范围内。
|
||
|
||
### 负步长范围
|
||
|
||
范围也可以倒计数。如果我们将步长设置为负数,范围将向后计数。如果我们想倒计数,我们需要确保`start`的值比`end`的值大*。这里有一个例子:*
|
||
|
||
```py
|
||
for i in range(5, 0, -1):
|
||
print(i, end=" ")
|
||
|
||
# Output will be:
|
||
# 5 4 3 2 1
|
||
```
|
||
|
||
我们从 5 开始倒数到 0。因为范围不包括结束值本身,所以不包括 0。如果要包含 0,需要将`end`设置为-1。
|
||
|
||
## Python 范围如何工作
|
||
|
||
现在,您已经看到了 range 的作用,我将告诉您 range 如何在内部工作,以便更好地理解它们。实际上,它没有多少魔力!
|
||
|
||
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these *free* articles, I hope you understand! **Support me by disabling your adblocker on my website** or, alternatively, **[buy me some coffee](https://www.buymeacoffee.com/pythonland)**. It's much appreciated and allows me to keep working on this site!
|
||
|
||
范围是一个可迭代的对象,这个对象可以返回一个迭代器来跟踪它的当前状态。假设我们用调用`range(3)`创建一个范围。这将返回一个具有我们所请求的设置的 range 类型的对象。在内部,Python 范围将设置以下内部变量:
|
||
|
||
* 起始值为 0,
|
||
* 结束值为 3,
|
||
* 并将步长值设置为默认值 1。
|
||
|
||
当我们开始迭代一个 range 对象时,range 对象将返回一个迭代器。这个迭代器使用上面定义的值,但是有一个额外的值:一个计数器变量`i`,默认情况下初始化为 0,并在每次迭代中增加。
|
||
|
||
每次调用迭代器来获取下一个数字时,range 都会查看其内部状态,并计算要返回的下一个数字。下一个值是这样计算的:
|
||
|
||
`next = start + step*i`。
|
||
|
||
默认情况下,范围的步长为 1:
|
||
|
||
* 所以在第一次调用时,进行的计算是这样的:`0 + 1*0 == 0`。Range 返回 0,计数器`i`加 1,使`i == 1`
|
||
* 在第二次调用时,它使用其当前状态进行相同的计算:`0 + 1*1 = 1`。它再次将计数器`i`增加到 2。
|
||
* 第三次调用返回 2,因为`0 + 1*2 == 2`。
|
||
* 我们现在已经到达了范围的尽头。正如我在关于 [Python 迭代器](https://python.land/deep-dives/python-iterator)的文章中所解释的,对迭代器的任何后续调用都将返回一个`StopIteration` [异常](https://python.land/deep-dives/python-try-except),表明不再有条目。
|
||
|
||
如果用`range(0, 5, 2)`给你的范围一个步长 2,你可以重复上面的计算,看到返回值现在是:0 和 2。
|
||
|
||
### 用代码演示
|
||
|
||
我们可以用一些代码来演示这个过程。在下面的例子中,我们首先创建一个 range 对象。接下来,我们使用内置的`iter()`函数从 range 对象手动请求一个迭代器。我们开始使用另一个内置函数从它那里请求下一个值:`next()`。在 3 次调用之后,您看到 iterable 被耗尽,并开始引发`StopIteration`异常:
|
||
|
||
```py
|
||
my_range = range(0, 3)
|
||
|
||
# Obtain the iterable. In a loop, this manual
|
||
# step is not needed
|
||
my_iter = iter(my_range)
|
||
|
||
# Request values from the iterable
|
||
|
||
print(next(my_iter))
|
||
# 0
|
||
|
||
print(next(my_iter))
|
||
# 1
|
||
|
||
print(next(my_iter))
|
||
# 2
|
||
|
||
print(next(my_iter))
|
||
# throws a StopIteration exception
|
||
```
|
||
|
||
如果您愿意,您也可以自己运行这段代码:
|
||
|
||
[https://crumb . sh/embed/C4 dmggj 56 qev](https://crumb.sh/embed/c4DmGj56QeV)
|
||
|
||
### Python 范围:Iterable 还是 iterator?
|
||
|
||
range 对象是可迭代的,这意味着它是一个可以返回迭代器的对象。正如你在上面的例子中看到的,我们可以用 iter()函数手动获得这个迭代器。在 for 循环中使用时,循环会自动请求迭代器,一切都会得到处理。
|
||
|
||
range 对象(iterable)将在每次调用时返回一个新的迭代器。因此,尽管这不是我们通常使用范围的方式,但我们实际上可以通过向 range 对象请求新的迭代器来继续使用它。如果你想详细理解这一点,请阅读 [iterables 和 iterators](https://python.land/deep-dives/python-iterator) 。
|
||
|
||
## Python 范围 vs 列表
|
||
|
||
与列表相比,范围提供了一个很大的优势:它们需要一个恒定的、可预测的内存量,因为它们是动态计算的。一个范围需要跟踪的只是开始、停止、结束和迭代计数器。因此范围(1000)需要与范围(1)相同的内存量。
|
||
|
||
相比之下:值为 0 到 999 的列表比只有值 1 的列表占用的内存多一千倍。
|
||
|
||
在 Python 2 中,情况并非如此。常规范围将具体化为一个实际的数字列表。在某个时候,xrange 被引入。在 Python 3 中,xrange 将成为范围的缺省值,旧的物化范围被丢弃了。此外,还增加了一些额外的特性,比如切片和比较范围的能力。
|
||
|
||
名称 xrange 在 Python 3 中不存在。如果您遇到它,您可能会看到 Python 2 代码。我写了一篇关于这个主题的文章,如果你想把它转换成 Python 3 代码的话。
|
||
|
||
## 实践中的 Python 范围
|
||
|
||
下面是一些常见的和不太常见的操作,除了在循环中使用它们之外,在实践中还可能用到。
|
||
|
||
### 将范围转换为列表
|
||
|
||
我们了解到范围是计算出来的,而列表是物化的。有时你想从一个范围中创建一个列表。你可以用 for 循环来实现。或者,更 Python 化更快:通过使用 [Python 列表理解](https://python.land/deep-dives/list-comprehension)。然而,最好的方法是使用`list()`函数,它可以将任何可迭代对象转换成列表,包括范围:
|
||
|
||
```py
|
||
my_list = list(range(0, 3))
|
||
print(my_list)
|
||
# [0, 1, 2]
|
||
```
|
||
|
||
### 成员资格测试
|
||
|
||
范围的行为类似于常规集合,因此我们也可以测试它们是否属于某个值。例如,要测试某个数字是否属于某个范围,您可以这样做:
|
||
|
||
```py
|
||
my_number = 10
|
||
print(my_number in range(0, 11))
|
||
# True
|
||
```
|
||
|
||
### 范围切片
|
||
|
||
虽然我不确定你是否需要它(我从未在实践中使用过),但这是一个非常酷的特性。就像您可以分割列表和其他具有序列类型的对象一样,您也可以分割 range 对象。这样做时,您可能会期望范围被具体化为一个列表。事实并非如此。相反,会计算并返回一个新的 range 对象,正如您在以下关于 [Python REPL](https://python.land/introduction-to-python/the-repl) 的示例中所看到的:
|
||
|
||
```py
|
||
>>> range(0, 5)[2:4]
|
||
range(2, 4)
|
||
>>> range(0, 5)[4:2:-1]
|
||
range(4, 2, -1)
|
||
>>> range(0, 10)[2:8:2]
|
||
range(2, 8, 2)
|
||
```
|
||
|
||
### 比较范围
|
||
|
||
最后,范围也可以比较。比较范围时,它们作为序列进行比较。您可能希望 Python 只检查它们是否是同一个对象。在 Python 3.3 问世之前,情况一直如此。此后,产生相同序列的两个不同范围在 Python 中将被视为相等。
|
||
|
||
以下是 REPL 的一些例子:
|
||
|
||
```py
|
||
>>> range(0, 2) == range(0, 3)
|
||
False
|
||
>>> range(0) == range(4, 2)
|
||
True
|
||
```
|
||
|
||
在最后一个例子中,开始为 4,结束为 2 的范围将导致长度为零的范围,因此它等于 a `range(0)`。
|
||
|
||
## 结论
|
||
|
||
您已经了解了 Python 的范围以及如何在 for 循环中使用它。不仅如此;您还了解了产品系列的内部运作方式。除了这些基础知识之外,您还学习了如何将一个范围转换为一个列表,范围可以进行比较,测试成员资格,我们甚至可以对范围进行切片。
|
||
|
||
如果您想了解更多关于产品系列的信息,您可能会对以下资源感兴趣:
|
||
|
||
* 我关于 [Python 迭代器和可迭代性的文章](https://python.land/deep-dives/python-iterator)
|
||
* 关于靶场的官方文件
|
||
* Trey Hunner 的这篇文章详细解释了为什么 range 不是迭代器(但它是可迭代的)* |