为什么切片和范围上限是独占的?

wap*_*p26 33 python language-design slice

免责声明:我不是问是否在上限stop的争论slice()以及range()为异或如何使用这些功能.

对函数rangeslice函数的调用以及切片表示法[start:stop]都是指整数集.

range([start], stop[, step])
slice([start], stop[, step])
Run Code Online (Sandbox Code Playgroud)

在所有这些中,stop排除整数.

我想知道为什么语言是这样设计的.

是它使stop当等于在表示整数集合元素的数量start等于0或省略?

它有:

for i in range(start, stop):
Run Code Online (Sandbox Code Playgroud)

看起来像下面的C代码?

for (i = start ; i < stop; i++) {
Run Code Online (Sandbox Code Playgroud)

Too*_*mai 34

文件意味着这有几个有用的属性:

word[:2]    # The first two characters
word[2:]    # Everything except the first two characters
Run Code Online (Sandbox Code Playgroud)

这是切片操作的有用不变量:s[:i] + s[i:]equals s.

对于非负索引,切片的长度是索引的差异,如果两者都在边界内.例如,长度word[1:3]2.

我认为我们可以假设范围函数的一致性相同.

  • 让我感到困惑的一件事是,对于数组 x,x[-1] 指的是最后一个元素,x[-2:-1] 不是指最后 2 个元素,而只是倒数第二个元素元素。特别是对于 Ruby 程序员,这是一个常见的陷阱,因为您习惯于将 -1 作为最后一个元素,而 .. 表示法是包含性的,即 x[-2..-1] 返回最后 2 个元素。python 冒号 ':' 实际上是红宝石三点 '...' (3认同)

Nig*_*nel 14

以下是一些Google+用户的意见:

[...]我被半开放间隔的优雅所左右.特别是不变量,当两个切片相邻时,第一个切片的结束索引是第二个切片的起始索引太漂亮而不能忽略.例如,假设您在索引i和j处将字符串拆分为三个部分 - 部分将是[:i],a [i:j]和[j:].


Lev*_*von 9

尽管如此,这个问题有点晚了,这会试图回答你问题的原因 -

部分原因是因为我们在寻址内存时使用从零开始的索引/偏移.

最简单的例子是一个数组.可以将"6项数组"视为存储6个数据项的位置.如果这个数组的起始位置是在内存地址100,那么数据,比如6个字符'apple\0',就像这样存储:

memory/
array      contains
location   data
 100   ->   'a'
 101   ->   'p'
 102   ->   'p'
 103   ->   'l'
 104   ->   'e'
 105   ->   '\0'
Run Code Online (Sandbox Code Playgroud)

所以对于6个项目,我们的索引从100到105.地址是使用base + offset生成的,所以第一项是基本内存位置 100 + 偏移 0(即100 + 0),第二项是100 + 1,第三项在100 + 2,...,直到100 + 5是最后一个位置.

这是我们使用基于零的索引的主要原因,并导致语言结构,如forC中的循环:

for (int i = 0; i < LIMIT; i++)
Run Code Online (Sandbox Code Playgroud)

或者在Python中:

for i in range(LIMIT):
Run Code Online (Sandbox Code Playgroud)

当您使用像C这样的语言进行编程时,您可以更直接地处理指针,或者更加直接地处理指针,这种基本+偏移方案变得更加明显.

由于上述原因,许多语言结构自动使用从startlength-1的范围.

你可能会发现这篇关于维基百科上基于零的编号很有意思的文章,以及来自Software Engineering SE的这个问题.

示例:

在C中,例如,如果你有一个数组ar,你下标它,因为ar[3]它实际上相当于获取数组的(基地)地址ar并添加3到它=> *(ar+3)这可以导致像这样的代码打印数组的内容,显示简单基数+偏移方法:

for(i = 0; i < 5; i++)
   printf("%c\n", *(ar + i));
Run Code Online (Sandbox Code Playgroud)

真的相当于

for(i = 0; i < 5; i++)
   printf("%c\n", ar[i]);
Run Code Online (Sandbox Code Playgroud)


jam*_*lin 7

这是另一个理由,即独占上限是一种更为理智的方法:

假设您希望编写一个函数,将某些变换应用于列表中项目的子序列.如果间隔是按照您的建议使用包含上限,您可能会天真地尝试将其写为:

def apply_range_bad(lst, transform, start, end):
     """Applies a transform on the elements of a list in the range [start, end]"""
     left = lst[0 : start-1]
     middle = lst[start : end]
     right = lst[end+1 :]
     return left + [transform(i) for i in middle] + right
Run Code Online (Sandbox Code Playgroud)

乍一看,这似乎是直截了当的,但不幸的是,这是巧妙的错误.

如果:

  • start == 0
  • end == 0
  • end < 0

?通常,您可能会考虑更多边界情况.谁想浪费时间思考所有这些?(这些问题的产生是因为通过使用包含的下限和上限,没有固有的方法来表示空间隔.)

相反,通过使用上限是独占的模型,将列表划分为单独的切片更简单,更优雅,因此更不容易出错:

def apply_range_good(lst, transform, start, end):
     """Applies a transform on the elements of a list in the range [start, end)"""
     left = lst[0:start]
     middle = lst[start:end]
     right = lst[end:]
     return left + [transform(i) for i in middle] + right
Run Code Online (Sandbox Code Playgroud)

(注意,apply_range_good它不会变换lst[end];它也会被视为end一个独占的上限.试图使它使用包含上限仍然会有我之前提到的一些问题.道德是包容性上限通常是麻烦的. )

(大部分改编自我的一篇关于另一种脚本语言的包容性上限的旧帖子.)


Won*_*Hau 5

优雅VS显而易见

老实说,我认为Python中的切片方式是违反直觉的,实际上是在通过更多的大脑处理来交易所谓的优雅,这就是为什么您可以看到这篇StackOverflow文章具有超过2K的赞成票的原因,我认为这是因为有很多人不了解它。

例如,以下代码已经使许多Python新手感到头疼。

x = [1,2,3,4]
print(x[0:1])
# Output is [1]
Run Code Online (Sandbox Code Playgroud)

不仅难以处理,而且也难以正确解释,例如,上面代码的解释将采用第零个元素,直到第一个元素之前的元素

现在看看使用上限的Ruby。

x = [1,2,3,4]
puts x[0..1]
# Output is [1,2]
Run Code Online (Sandbox Code Playgroud)

坦率地说,我真的认为Ruby切片方式对大脑更好。

当然,当您基于索引将列表分为两部分时,排他的上限方法将使代码看起来更好。

# Python
x = [1,2,3,4]
pivot = 2
print(x[:pivot]) # [1,2]
print(x[pivot:]) # [3,4]
Run Code Online (Sandbox Code Playgroud)

现在让我们看一下包容性上限方法

# Ruby
x = [1,2,3,4]
pivot = 2
puts x[0..(pivot-1)] # [1,2]
puts x[pivot..-1] # [3,4]
Run Code Online (Sandbox Code Playgroud)

显然,代码不太优雅,但是这里不需要太多的脑力劳动。

结论

最后,这实际上是关于优雅VS明显的问题,Python的设计师更喜欢优雅而不是明显。为什么?因为PythonZen指出美丽比丑陋要好

  • 我同意零基索引(ZBI)*一开始*并不明显。我记得几十年前,当我第一次学习编程时,很多(很多 - 不*很多*)都对 ZBI 感到有点困惑。问题不在于排他性的上限概念,而在于该概念的使用没有得到解释。但一旦我弄清楚了这一点,它的用途就变得显而易见了!因此,也许“显而易见”是情人眼里出西施,或者用另一种(更优雅的:-)方式来说:显而易见的是,除非有人简单地表达出来,否则永远不会看到它。如果 Python 教科书和教程能简单地表达这一点就好了。 (2认同)