免责声明:我不是在询问 stop
和slice()
的上限range()
参数是否为独占或如何使用这些功能。
调用range
和slice
函数以及切片表示法[start:stop]
都是指整数集。
range([start], stop[, step])
slice([start], stop[, step])
在所有这些中,stop
整数被排除在外。
我想知道为什么语言就是这样设计的。
当stop
等于0或被省略时,是否使start
等于所表示的整数集中的元素数?
是否有:
for i in range(start, stop):
看起来像下面的C代码?
for (i = start ; i < stop; i++) {
答案 0 :(得分:32)
documentation意味着它有一些有用的属性:
word[:2] # The first two characters
word[2:] # Everything except the first two characters
以下是切片操作的有用不变量:
s[:i] + s[i:]
等于s
。对于非负索引,切片的长度是索引的差异,如果两者都在边界内。例如,
word[1:3]
的长度为2
。
我认为我们可以假设范围函数的一致性是相同的。
答案 1 :(得分:11)
以下是某些Google+用户的opinion:
[...]我被半开的间隔的优雅所左右。特别是 不变的,当两个切片相邻时,第一个切片的结束 index是第二个切片的起始索引太漂亮了 忽视。例如,假设您将字符串拆分为三个部分 索引i和j - 部分将是[:i],a [i:j]和a [j:]。
答案 2 :(得分:8)
这个问题有点晚了,但是,这会尝试回答你问题的 为什么 - 部分:
部分原因是因为我们在寻址内存时使用从零开始的索引/偏移。
最简单的例子是一个数组。可以将“6项数组”视为存储6个数据项的位置。如果这个数组的起始位置是在内存地址100,那么数据,比如6个字符'apple \ 0',就像这样存储:
memory/
array contains
location data
100 -> 'a'
101 -> 'p'
102 -> 'p'
103 -> 'l'
104 -> 'e'
105 -> '\0'
因此对于6个项目,我们的索引从100到105.地址是 使用 base + offset 生成,因此第一项位于基本内存位置 100 + offset 0 (即100 + 0),第二个在100 + 1,第三个在100 + 2,......,直到100 + 5是最后一个位置。
这是我们使用基于零的索引并导致的主要原因
语言结构,例如C:中的for
循环
for (int i = 0; i < LIMIT; i++)
或在Python中:
for i in range(LIMIT):
使用C语言进行编程时,处理指针 这个基础+偏移方案更直接,或更集合 变得更加明显。
由于上述原因,许多语言结构会自动使用 start 到 length-1 的范围。
您可能会发现维基百科上Zero-based numbering上的这篇文章很有趣,还有this question from Software Engineering SE。
实施例:
在C中,例如,如果你有一个数组ar
,你将其下标为ar[3]
,这实际上等同于获取数组ar
的(基本)地址并添加{{1 }} to it =&gt; 3
这可以导致这样的代码打印数组的内容,显示简单的基本+偏移方法:
*(ar+3)
真的相当于
for(i = 0; i < 5; i++)
printf("%c\n", *(ar + i));
答案 3 :(得分:6)
这是另一个理由,即独占上限是一种更为理智的方法:
假设您希望编写一个函数,将某些变换应用于列表中项目的子序列。如果间隔是按照您的建议使用包含上限,您可能会天真地尝试将其写为:
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
乍一看,这似乎是直截了当的,但不幸的是,这是巧妙的错误。
如果出现以下情况:
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
(请注意apply_range_good
不会转换lst[end]
;它也会将end
视为独占上限。尝试使其使用包含上限仍会有一些我之前提到的问题。道德是包容性的上限通常是麻烦的。)
(大部分改编自an old post of mine about inclusive upper-bounds in another scripting language。)
答案 4 :(得分:4)
老实说,我认为在Python中切片的方式是非常反直觉的,它实际上是通过更多脑处理来交换所谓的 elegant-ness ,这就是为什么你可以看到this StackOverflow article有超过2K的upvotes,我认为这是因为有很多人不理解它。
例如,以下代码已经引起了许多Python新手的头痛。
RingerBroadcastReceiver receiver = new RingerBroadcastReceiver();
IntentFilter filter = new IntentFilter(
AudioManager.RingerModeChangedAction);
RegisterReceiver(receiver, filter);
不仅难以处理,也难以正确解释,例如,上面代码的解释是将第0个元素带到第一个元素之前的元素。
现在看看使用上限的Ruby。
x = [1,2,3,4]
print(x[0:1])
# Output is [1]
坦率地说,我真的认为Ruby的切片方式对大脑来说更好。
当然,当您根据索引将列表拆分为2个部分时,独占上限方法会产生更好看的代码。
x = [1,2,3,4]
puts x[0..1]
# Output is [1,2]
现在让我们看看包容性上限方法
# Python
x = [1,2,3,4]
pivot = 2
print(x[:pivot]) # [1,2]
print(x[pivot:]) # [3,4]
显然,代码不那么优雅,但是这里没有太多的脑处理。
最后,它真的是关于优雅的VS Obvious-ness的问题,而Python的设计师更喜欢优雅而不是明显的优势。为什么?因为Zen of Python表示 Beautiful比丑陋更好。
答案 5 :(得分:-1)
这种上限排除极大地提高了代码理解。我希望它适用于其他语言。