为什么范围(开始,结束)不包括结束?

时间:2010-12-21 22:45:46

标签: python range

>>> range(1,11)

给你

[1,2,3,4,5,6,7,8,9,10]

为什么不是1-11?

他们是否只是随意决定这样做,或者它有什么价值我没有看到?

10 个答案:

答案 0 :(得分:206)

因为更常见的是调用range(0, 10)返回[0,1,2,3,4,5,6,7,8,9],其中包含10个等于len(range(0, 10))的元素。请记住,程序员更喜欢基于0的索引。

另外,请考虑以下常见代码段:

for i in range(len(li)):
    pass

如果range()完全len(li),那么这会有问题吗?程序员需要明确地减去1.这也遵循程序员更喜欢for(int i = 0; i < 10; i++)而非for(int i = 0; i <= 9; i++)的共同趋势。

如果您经常以1开头调用范围,则可能需要定义自己的函数:

>>> def range1(start, end):
...     return range(start, end+1)
...
>>> range1(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

答案 1 :(得分:21)

独家范围确实有一些好处:

首先,range(0,n)中的每个项目都是长度为n的列表的有效索引。

同样range(0,n)的长度为n,而不是n+1,其中包含范围。

答案 2 :(得分:19)

虽然这里有一些有用的算法解释,但我认为可能有助于添加一些简单的“现实生活”推理,说明为什么它以这种方式工作,我发现在向年轻新人介绍这个主题时有用:

像“范围(1,10)”这样的东西可能会因为认为这对参数代表“开始和结束”而产生混淆。

实际上是开始并“停止”。

现在,如果 的“结束”值,那么,是的,您可能希望该数字将作为序列中的最终条目包含在内。但这不是“结束”。

其他人错误地将该参数称为“count”,因为如果你只使用'range(n)'那么它当然会迭代'n'次。添加start参数时,此逻辑会中断。

所以关键是要记住它的名字:“停止”。 这意味着,当达到时,迭代将立即停止。 之后

因此,虽然“开始”确实代表了要包含的第一个值,但在达到“停止”值时,它“打破”而不是在停止之前继续处理“那个”。

我用来向孩子们解释这个问题的一个类比是,具有讽刺意味的是,它比孩子更好!它不会在之后停止它会立即停止而不会完成它正在做的事情。 (他们得到了这个;))

另一个比喻 - 当你驾驶汽车时,你没有 传递 停止/收益/'让路'的标志,并最终坐在旁边的某个地方,或后面,你的车。从技术上讲,当你停下来时,你仍然没有达到它。它不包括在“你旅行中传递的东西”中。

我希望其中一些有助于向Pythonitos / Pythonitas解释!

答案 3 :(得分:18)

与零基索引和len()结合使用效果很好。例如,如果列表x中有10个项目,则编号为0-9。 range(len(x))为您提供0-9。

当然,人们会告诉你,for item in xfor index, item in enumerate(x)而不是for i in range(len(x))更像是Pythonic。

切片也是如此:foo[1:4]foo的第1-3项(请记住,由于从零开始的索引,第1项实际上是第2项)。为了保持一致性,它们应该以相同的方式工作。

我认为它是:“你想要的第一个号码,然后是想要的第一个号码。”如果你想要1-10,你不想要的第一个数字是11,所以它是range(1, 11)

如果在特定的应用程序中变得很麻烦,那么编写一个小的辅助函数就很容易了,它会在结束索引中加1并调用range()

答案 4 :(得分:11)

范围的长度是最高值减去最低值。

它非常类似于:

for (var i = 1; i < 11; i++) {
    //i goes from 1 to 10 in here
}

采用C风格的语言。

也像Ruby的范围:

1...11 #this is a range from 1 to 10

但是,Ruby认识到很多时候你想要包含终端值并提供替代语法:

1..10 #this is also a range from 1 to 10

答案 5 :(得分:11)

它对于分割范围也很有用; range(a,b)可以分为range(a, x)range(x, b),而在包含范围的情况下,您可以编写x-1x+1。虽然您很少需要拆分范围,但您确实倾向于经常拆分列表,这是切片列表l[a:b]包括第a个元素而不是第b个元素的原因之一。然后具有相同属性的range使其非常一致。

答案 6 :(得分:4)

考虑代码

for i in range(10):
    print "You'll see this 10 times", i

这个想法是你得到一个长度y-x的列表,你可以(如上所述)迭代。

the python docs上读取范围 - 他们认为for循环迭代是主要用例。

答案 7 :(得分:4)

基本上在python range(n)中重复n次,这是排他性,这就是为什么它在打印时不给出最后一个值的原因,我们可以创建一个函数来给出 包含值,表示它还将打印范围中提到的最后一个值。

def main():
    for i in inclusive_range(25):
        print(i, sep=" ")


def inclusive_range(*args):
    numargs = len(args)
    if numargs == 0:
        raise TypeError("you need to write at least a value")
    elif numargs == 1:
        stop = args[0]
        start = 0
        step = 1
    elif numargs == 2:
        (start, stop) = args
        step = 1
    elif numargs == 3:
        (start, stop, step) = args
    else:
        raise TypeError("Inclusive range was expected at most 3 arguments,got {}".format(numargs))
    i = start
    while i <= stop:
        yield i
        i += step


if __name__ == "__main__":
    main()

答案 8 :(得分:1)

在许多情况下进行推理就更方便了。

基本上,我们可以将范围视为startend之间的间隔。如果为start <= end,则它们之间的间隔长度为end - start。如果实际上将len定义为长度,那么您将:

len(range(start, end)) == start - end

但是,我们计算范围内包含的整数,而不是测量间隔的长度。为了使上述属性为真,我们应包括一个端点,而排除另一个端点。

添加step参数就像引入长度单位一样。在这种情况下,您会期望

len(range(start, end, step)) == (start - end) / step

长度。要获得计数,您只需使用整数除法即可。

答案 9 :(得分:0)

python 中的 range(n) 从 0 返回到 n-1。 range(1,n) 分别从 1 到 n-1。 因此,如果您想省略第一个值并获得最后一个值 (n),您可以使用以下代码非常简单地完成。

for i in range(1, n + 1):
        print(i) #prints from 1 to n