【问题标题】:Why does range(start, end) not include end? [duplicate]为什么范围(开始,结束)不包括结束? [复制]
【发布时间】:2022-12-19 20:27:33
【问题描述】:
>>> range(1,11)

给你

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

为什么不是 1-11?

他们是随机决定那样做还是有一些我没有看到的价值?

【问题讨论】:

  • 阅读 Dijkstra,ewd831
  • 基本上,您是在为另一组选择一组差错错误。一组更有可能导致循环提前终止,另一组可能导致异常(或其他语言的缓冲区溢出)。一旦你写了一堆代码,你会发现 range() 的行为选择更常见
  • 链接到 Dijkstra,ewd831:cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF
  • @andreasdr 但即使装饰性论点是有效的,Python 的方法不会引入新的可读性问题吗?在常用英语中,术语“范围”表示某物范围某物一些东西——比如一个间隔。 len(list(range(1,2))) 返回 1 而 len(list(range(2))) 返回 2 是你真正需要学习消化的东西。
  • 如果有人说他们想要从绿色到红色的一系列颜色,那么很少有人会说他们不想要红色。所以英语单词范围不是合适的单词。这不会改变,但我认为这是 python 是一种明智语言的盔甲上的一个缝隙。

标签: python range


【解决方案1】:

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

另外,请考虑以下公共代码 sn-p:

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]

【讨论】:

  • 如果那是推理,那么参数不是range(start, count)吗?
  • @shogun 起始值默认为0,即range(10)等同于range(0, 10)
  • 您的range1 不适用于步长与1 不同的范围。
  • 你解释说 range(x) 应该从 0 开始,x 将是“范围的长度”。好的。但是你没有解释为什么 range(x,y) 应该以 x 开始并以 y-1 结束。如果程序员想要一个 i 从 1 到 3 的 for 循环,他必须显式地加 1。这真的是为了方便吗?
  • for i in range(len(li)): 是一种反模式。应该使用enumerate
【解决方案2】:

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

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

它实际上是启动和“停止”。

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

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

所以重点是记住它的名字:“停止”。 这意味着它是到达时迭代将立即停止的点。不是那一点。

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

我在向孩子们解释这一点时使用的一个类比是,具有讽刺意味的是,它比孩子们表现得更好!它不会停止它应该 - 它立即停止而没有完成它正在做的事情。 (他们明白了;))

另一个类比——当你开车时,你不经过一个停止/让行/“让路”的标志,最后它坐在你的车旁边或后面的某个地方。从技术上讲,当你停下来时你仍然没有达到它。它不包括在“你在旅途中传递的东西”中。

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

【讨论】:

  • 这个解释比较直观。谢谢
  • @bzip2,Python 不是猪,它是蛇。你所说的“不一致”和“缺陷”不是:它们是开发人员做出的设计选择,在整个语言中始终如一地进行,并允许数百万程序员解决他们的任务。如果您不喜欢它,请使用提供包含范围的扩展或切换到另一种语言。你的 cmets 对理解 Python 没有帮助,反而冒犯了社区。它们还表明您未能理解独占间隔的性质,因为如果您在 9 点到 11 点开会,那么 11 点您将有空。
  • 喜欢停车标志类比,抱歉偷了它:)
【解决方案3】:

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

一方面,range(0,n) 中的每一项都是长度为n 的列表的有效索引。

另外 range(0,n) 的长度为 n,而不是包含范围的 n+1

【讨论】:

    【解决方案4】:

    它与基于零的索引和 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 项实际上是第二项)。为了保持一致性,它们都应该以相同的方式工作。

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

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

    【讨论】:

    • 同意切片。 w = 'abc'; w[:] == w[0:len(w)]; w[:-1] == w[0:len(w)-1];
    • def full_range(start,stop): return range(start,stop+1) ## helper function
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

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

      它与以下内容非常相似:

      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
      

      【讨论】:

        【解决方案7】:

        考虑代码

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

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

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

        【讨论】:

        • 最简单的解释。登录只是为了投票
        【解决方案8】:

        基本上在 python 中 range(n) 迭代 n 次,这是排他性的,这就是为什么它在打印时不给出最后一个值,我们可以创建一个函数来给出 inclusive value 这意味着它还将打印范围中提到的最后一个值。

        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()
        

        【讨论】:

        • 为了避免无限循环可能带来的意外,我建议改进这段代码,使其在步长值为负的情况下也能正常工作。
        【解决方案9】:

        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
        

        【讨论】:

        • OP 知道如何获得额外的价值,他们正在询问默认情况下不包含它的原因。
        【解决方案10】:

        在许多情况下,推理起来更方便。

        基本上,我们可以将范围视为startend 之间的区间。如start &lt;= end,则他们之间的间隔长度为end - start。如果 len 实际上被定义为长度,您将拥有:

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

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

        添加step参数就像引入了一个长度单位。在那种情况下,你会期望

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

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

        【讨论】:

        • 这些对 Python 的不一致性的辩护很滑稽。如果我想要两个数字之间的间隔,为什么我要使用减法而不是间隔来求差?对开始和结束位置使用不同的索引约定是不一致的。为什么需要写“5:22”才能获得位置 5 到 21?
        • 它不是 Python 的,它在所有方面都非常普遍。在 C、Java、Ruby 中,随你便
        • 我的意思是说索引很常见,而不是其他语言必须具有完全相同的对象类型
        • @Arseny 为 Ruby 辩护,这不是真的。您可以在 Ruby 中构造包含和排除范围:(3..5).include?(5) =&gt; true(3...5).include?(5) =&gt; false。数组切片是显式和包容性的:[0,1,2,3,4].slice(0,2) =&gt; [0, 1]。您甚至可以构建开放范围:r = 42..; r.include?(Float::INFINITY) =&gt; true
        • @AndreasGebhard,毫无疑问,在某些情况下这很方便。例如,Scala 同时具有 a to ba until b。我的观点是,排除范围的右端是常见的做法,并不是任何不一致。此外,从历史上看,&lt; 比较对于处理器来说比 &lt;= 比较更快
        【解决方案11】:

        范围在 python 中的两个主要用途。所有的事情都倾向于落入一个或另一个

        1. 整数。使用内置:range(start, stop, step)。包含 stop 意味着结束步骤对于一般情况来说是不对称的。考虑range(0,5,3)。如果默认行为在最后输出 5,它就会被破坏。
        2. 浮点数。这是用于数字用途(有时它也恰好是整数)。然后使用numpy.linspace

        【讨论】:

          猜你喜欢
          • 2011-05-29
          • 2019-07-06
          • 1970-01-01
          • 2012-10-15
          • 1970-01-01
          • 1970-01-01
          • 2011-07-10
          • 1970-01-01
          相关资源
          最近更新 更多