wap*_*p26 33 python language-design slice
免责声明:我不是问是否在上限stop
的争论slice()
以及range()
为异或如何使用这些功能.
对函数range
和slice
函数的调用以及切片表示法[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:]
equalss
.对于非负索引,切片的长度是索引的差异,如果两者都在边界内.例如,长度
word[1:3]
是2
.
我认为我们可以假设范围函数的一致性相同.
尽管如此,这个问题有点晚了,这会试图回答你问题的原因 -
部分原因是因为我们在寻址内存时使用从零开始的索引/偏移.
最简单的例子是一个数组.可以将"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是最后一个位置.
这是我们使用基于零的索引的主要原因,并导致语言结构,如for
C中的循环:
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这样的语言进行编程时,您可以更直接地处理指针,或者更加直接地处理指针,这种基本+偏移方案变得更加明显.
由于上述原因,许多语言结构自动使用从start到length-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)
这是另一个理由,即独占上限是一种更为理智的方法:
假设您希望编写一个函数,将某些变换应用于列表中项目的子序列.如果间隔是按照您的建议使用包含上限,您可能会天真地尝试将其写为:
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
一个独占的上限.试图使它使用包含上限仍然会有我之前提到的一些问题.道德是包容性上限通常是麻烦的. )
(大部分改编自我的一篇关于另一种脚本语言的包容性上限的旧帖子.)
老实说,我认为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的设计师更喜欢优雅而不是明显。为什么?因为Python的Zen指出美丽比丑陋要好。