• Python中的Range()函数使用指南
  • 发布于 1个月前
  • 66 热度
    0 评论
  • 乔治
  • 1 粉丝 4 篇博客
  •   
当需要执行特定次数的操作时,Python内置的range函数十分方便。读罢本文,你将:
1.理解Python的range函数是如何工作的
2.了解Python 2和Python 3中的实现方式有何不同
3.看过了不少range()函数操作实例
4.有能力解决它的一些局限性
让我们开始吧!

range()函数的历史
虽然Python 2的range()和Python3中的range()共享了一个名字,但它们是完全不同的家伙。事实上,Python 3中的range()只是Python 2中被称为xrange的函数的重命名版本。最初range()和xrange()都生成可以用for循环迭代的数字,但是前者同时生成这些数字的列表,而后者则懒洋洋地生成数字,这意味着后者只能在需要时一次返回一个数字。


巨大的列表会占用内存,所以xrange()取代range()的名称和一切都不奇怪。您可以在PEP 3100中了解更多关于这个决定和xrange() vs range()的背景信息。

注:PEP代表Python改善提议。PEPs是涵盖广泛主题的文档,其主题包括提出的新特性、风格、管理和哲学。
他们有很多。PEP 1解释了它们是如何工作的,并且是一个入门的好地方。在本文其余部分中,您将使用Python 3中的range()函数。让我们开始吧!


让我们来循环

在深入研究range()如何工作之前,我们需要先看看循环是如何工作的。循环是计算机科学中的一个重要概念。掌握循环是成为一名优秀程序员的第一步。
下面是Python中for循环的一个例子:
captains = ['Janeway', 'Picard', 'Sisko']
for captain in captains:
    print(captain)
输出如下:
Janeway
Picard
Sisko
正如你所见,for循环使您可以执行特定的代码块,多少次都没问题。在这种情况下,我们遍历一个船长舰长名单,打印他们的名字。虽然《星际迷航》很棒,但你可能想做的不仅仅是简单的圈出舰长的名单。有时,您只希望执行特定次数的代码块。循环可以帮助你做到这一点!
试运行下面的代码,其中的数字可以被3整除:
numbers_divisible_by_three = [3, 6, 9, 12, 15]
for num in numbers_divisible_by_three:
    quotient = num / 3
    print("{} divided by 3 is {}.".format(num, int(quotient)))
该循环的输出如下:
3 divided by 3 is 1.
6 divided by 3 is 2.
9 divided by 3 is 3.
12 divided by 3 is 4.
15 divided by 3 is 5.
这是我们想要的输出,所以循环充分地完成了工作,但是还有另一种方法通过使用range()获得相同的结果。
注:最后一个代码示例有一些字符串格式化。更多相关内容参见: Python String Formatting Best Practices 和 Python 3’s f-Strings: An Improved String Formatting Syntax (Guide)
到此你已对循环更熟悉了,接下来让我们看看如何使用range()来简化。

从range()开始
那么Python的range函数是如何工作的呢?简单地说,range()允许你在给定范围内生成一系列数字。你可以根据传递给函数的参数数量决定该系列数字的开始和结束位置,以及该系列数字与下一个数字之间的步长。
让我们在操作中先睹为快:
for i in range(3, 16, 3):
    quotient = i / 3
    print("{} divided by 3 is {}.".format(i, int(quotient)))
在这个for循环中,你可以轻松地创建可被3整除的一系列数字,而不必自己提供每个数字。

注:尽管这个例子适当的使用了range(),但通常不喜欢在for循环里中频繁使用range()。

例如,下面的range()用例,一般不被认为是Python风格的:

captains = ['Janeway', 'Picard', 'Sisko']
for i in range(len(captains)):
    print(captains[i])
range()非常适合创建数字的迭代,但是当你需要迭代可用in操作符完成循环的数据时,它不是最佳选择。

你可用下述三种方式调用range():
1.range(stop) 需要一个参数
2.range(start, stop) 需要两个参数
3.range(start, stop, step) 需要三个参数

range(stop)
当使用一个参数调用range()时,您将得到一系列数字,这些数字从0开始,包括直到(但不包括)stop的每个整数。
以下是实践中的情况:
for i in range(3):
    print(i)
循环的输出如下
0
1
2
结果显示:输出的所有数字都在0到3之间,但不包括提供的3。
range(start, stop)
当你用两个参数调用range()时,你不仅要决定一系列数字停止的位置,而且还要决定它从哪里开始,所以你不必总是在0点开始。可以使用range()来使用范围(A,B)生成从A到B的一系列数字。让我们来看看如何生成一个从1开始的范围。
尝试用两个参数调用range():
for i in range(1, 8):
    print(i)
输出如下:
1
2
3
4
5
6
7
到目前为止,非常好:您拥有从1(作为开始提供的数字)到但不包括8(作为停止提供的数字)的所有整数。但是如果您再添加一个参数,那么您就能够重现前面使用number_divisible_by_three列表时得到的输出。

range(start, stop, step)
当使用三个参数调用range()时,您不仅可以选择数字序列的开始和停止位置,还可以选择一个数字与下一个数字之间的步长。如果不提供step,那么range()将自动取step=1。
注:step可以是正数也可以是负数,但不能为0:
>>> range(1, 4, 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: range() arg 3 must not be zero
若以0为步长,将报错。现在你知道如何使用step,可以回到我们之前看到的循环除以3。
自己试试看:
for i in range(3, 16, 3):
    quotient = i / 3
    print("{} divided by 3 is {}.".format(i, int(quotient)))

输出将完全类似于本文前面看到的使用名为number_divisible_by_three的列表时的for循环的输出:
3 divided by 3 is 1.
6 divided by 3 is 2.
9 divided by 3 is 3.
12 divided by 3 is 4.
15 divided by 3 is 5.
正如你在这个例子中看到的,你可以使用step参数增加到一个更高的数值。这叫做递增

用range()实现递增
如果你想实现递增,那么你需要将step设为正数。为了解这个的现实意义,键入以下代码:
for i in range(3, 100, 25):
    print(i)
如果step取25,那么循环的输出将是这样的
3
28
53
78

你得到的数字比前一个数字大25——你提供的step。现在,你已经看到了如何向前迈进一个距离,现在是时候看看你如何向后递减。

用range()实现递减
如果你的step是正数,那么你会得到一系列不断递增的数字。如果你的step为负数,那么你会得到一系列递减的数字。这可以让你从数字中倒退过来。
在下面的示例中,step取-2。这意味着每个循环的递减步长为2
for i in range(10, -6, -2):
    print(i)
递减循环的输出如下:
10
8
6
4
2
0
-2
-4
你有一串数字,每一个都比前一个数字小2,这是你提供的step的绝对值。创建一个递减范围的最快速的方法是使用range(start,stop,step)。但是Python确实有一个内置的反函数。如果在reversed()中包装range(),则可以以相反的顺序打印整数
试试看:
for i in reversed(range(5)):
    print(i)
你将得到如下结果:
4
3
2
1
0
range()使迭代递减的数序列成为可能,而reversed()通常用于以相反的顺序对序列进行循环。
注:reversed()也与字符串一起工作。你可以在 How to Reverse a String in Python中了解更多关于字符串的reversed()的功能。

更深入使用range()
现在你知道了如何使用range()的基础知识,是时候深入挖掘一下了。
range()主要用于两个目的:
1.重复执行for循环的循环体指定次数
2.创建比使用列表或元组完成的更高效的整数迭代算法
第一种用法可能是最常用的,并且你可以找到一种比利用range()更高效的利用迭代工具的方法来构造迭代器。
下面是使用range时要记住的几个要点。
Python中range()是一种类型:
>>> type(range(3))
<class 'range'>
你可以按索引访问range()中的项,就像列表中的那样:
>>> range(3)[1]
1
>>> range(3)[2]
2
您甚至可以在range()上使用切片标记,但是REPL中的输出在一开始可能看起来有点奇怪:
>>> range(6)[2:5]
range(2, 5)
尽管这个输出看起来很奇怪,对一个range()切片,又返回另一个range()。
可以通过索引和切片来访问range()的元素,这一事实突出了一个重要事实:range()与列表不同,是惰性的,但不是迭代器。

Float和range()
您可能已经注意到,到目前为止我们所处理的所有数字都是整数,也称为整数。这是因为range()只能以整数作为参数。
浮点一词:
在Python中,如果一个数字不是整数,那么它就是一个浮点。整数和浮点之间有一些区别。
整数(int数据类型):
.是整数
·不包括小数点
·可以是正数、负数或0
浮点数(浮点数据类型):
·可以是包含小数点的任何数字
·可以是正的也可以是负的
尝试用浮点调用range(),看看会发生什么:
for i in range(3.3):
    print(i)
你应得到如下错误信息:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer

如果你需要找到一个可以让你使用浮点的解决方案,那么你可以使用NumPy。
 
使用NumPy
NumPy是第三方Python库。如果你要使用NumPy,第一步是检查你是否安装了它。
以下是你在REPL做到这一点的方法:
>>> import numpy
如果你得到了一个 ModuleNotFoundError,那么你需要安装它。为此,请转到命令行并输入pip install numpy。
一旦安装完毕,请输入以下内容:
import numpy as np
np.arange(0.3, 1.6, 0.3)

它的返回如下:
array([0.3, 0.6, 0.9, 1.2, 1.5])
如果要换行打印每个数字,可以执行以下操作:
import numpy as np
for i in np.arange(0.3, 1.6, 0.3):
    print(i)
输出如下:
0.3
0.6
0.8999999999999999
1.2
1.5

0.8999999999999999从哪来的?
计算机在二进制浮点数字中保存十进制浮点数字是困难的。这导致了各种意想不到的数字表示。
注:为了了解为什么有小数的问题,你可以看看这篇文章和Python文档。
您可能还想看一下十进制库,它在性能和可读性方面略有下降,但是允许您准确地表示十进制数。另一个选择是使用round(),您可以在 How to Round Numbers in Python中了解更多相关内容。请记住,round()有自己的        怪癖,可能会产生一些令人惊讶的结果!
这些浮点错误是否对你来说是个问题,取决于你正在解决的问题。误差将存在于第十六位小数处,绝大多数时候这是微不足道的。它们太小了,除非你正在计算卫星轨道或其他东西,否则你不必担心它。
或者,您也可以使用 np.linspace()。它本质上是相同的,但是使用不同的参数。使用 np.linspace(),可以指定start和end(包含两者)以及数组的长度(而不是step)。
例如,np.linspace(1, 4, 20) 给出了20个等距的数字:1.0, ..., 4.0. 另一方面, np.linspace(0, 0.5, 51) 给出0.00, 0.01, 0.02, 0.03, ..., 0.49, 0.50.
注:为了了解更多,您可以阅读 Look Ma, No For-Loops: Array Programming With NumPy and this handy和 NumPy reference.。
 
循环前行
现在,你已了解如何使用range()并处理其局限性。您还可以了解Python 2和Python 3之间的这个重要功能是如何演变的。下一次你需要执行一个特定的次数,胸有成竹!
用户评论