一道面试题引发的关于列表切片的思考

先贴上面试题:
现在有 a = [1,2,3,4,5,6] 不通过函数的形式实现列表的反转([6,5,4,3,2,1]) 并写出推导过程
关于列表切片,在官方给出的解释如下:
s[i:j:k] slice of s from i to j with step k
s is an instance of a mutable sequence type, t is any iterable object and x is an arbitrary object that meets any type and value restrictions imposed by s
也就是说对于可变序列s作切片操作时,i为起始位置,j为结束位置,k为步长。

先来看缺少步长的情况

1、我们先来个简单的例子:

s = ['西门彼得', '安得烈', '雅各布', '约翰', '腓力', '多马', '马太']
print(s[2:5])
# ['雅各布', '约翰', '腓力']

可见列表索引号是从0开始,切片中[2:5]是包含索引号为2的’雅各布’,但不包含索引号为5的’多码’
2、切片的高阶写法

s = ['西门彼得', '安得烈', '雅各布', '约翰', '腓力', '多马', '马太']
print(s[:5])
# ['西门彼得', '安得烈', '雅各布', '约翰', '腓力']

当我们不写冒号前的数字时默认从索引号0开始,那么聪明的你一定知道下列代码的输出结果

s = ['西门彼得', '安得烈', '雅各布', '约翰', '腓力', '多马', '马太']
print(s[4:])

对了,虽然这个列表的长度为7,但最大的索引号为6,故上述代码执行结果放在本文末尾①
那么如果冒号两边都不写会发生什么神奇的事呢?

s = ['西门彼得', '安得烈', '雅各布', '约翰', '腓力', '多马', '马太']
print(s[:])

执行结果放在文章末尾②
当我们以为切片的高阶写法到此为止时,那么我们太天真了,切片是Python区别于其他高级程序设计语言的高效率法门之一,我们可以取负写法
先来看这个列表
s = [‘西门彼得’, ‘安得烈’, ‘雅各布’, ‘约翰’, ‘腓力’, ‘多马’, ‘马太’]
其中s[0]=‘西门彼得’ s[1]=‘安得烈’ s[2]=‘雅各布’ s[3]=‘约翰’ s[4]=‘腓力’ s[5]=‘多马’ s[6]=‘马太’
请注意,'马太’的索引号除了用6表示,还可以用-1表示,依次类推,我们看一下程序输出

s = ['西门彼得', '安得烈', '雅各布', '约翰', '腓力', '多马', '马太']
print(s[6],s[-1],s[5],s[-2],s[0],s[-7])
# 马太 马太 多马 多马 西门彼得 西门彼得

我们发现列表中每一个元素都可以有两个索引号,这时候你对上述的高阶写法是否有了更美好的想法呢?!
我们将代码罗列如下:

s = ['西门彼得', '安得烈', '雅各布', '约翰', '腓力', '多马', '马太']
print(s[0:-1])
# ['西门彼得', '安得烈', '雅各布', '约翰', '腓力', '多马']
print(s[-4:-1])
# ['约翰', '腓力', '多马']
print(s[2:-2])
# ['雅各布', '约翰', '腓力']
print(s[-6:4])
# ['安得烈', '雅各布', '约翰']
print(s[2:-5])
# []
print(s[2:2])
# []
print(s[4:-6])
# []

从上述程序运行结果我们可知,在做列表切片时s[i:j:k] ,索引号i与索引号j之间一定要用元素,且包含i号元素但不包含j号元素,若i与j重合(例如2,3条代码)则返回空列表,若i在j右侧,也同样返回空值,因此我认为把s[i:j:k]改成s[LeftIndex:RightIndex:Step]会更形象,易于理解!

下面我们来讨论加入步长的情况

步长:一般指的是每次取的间隔,如下代码所示

s = ['西门彼得', '安得烈', '雅各布', '约翰', '腓力', '多马', '马太']
print(s[::2])
# ['西门彼得', '雅各布', '腓力', '马太']

冒号前所没写起始和终止位置,代表要求取全列表,第3个步长参数是2表示先取0号索引元素,然后依次取2,4,6共4个元素。
和索引号相同,步长也可取负,如:

s = ['西门彼得', '安得烈', '雅各布', '约翰', '腓力', '多马', '马太']
print(s[1:6:2])
# ['西门彼得', '雅各布', '腓力', '马太']
print(s[1:6:-2])
# []
print(s[6:1:-2])
# ['马太', '腓力', '雅各布']
print(s[-1:1:-2])

通过上面的代码我们发现当步长为正值时,切片s[LeftIndex:RightIndex:Step]中LeftIndex索引号元素必须在RightIndex索引号元素左边;当步长为负值时,LeftIndex索引号元素必须在RightIndex索引号元素右边,且返回结果是依次从右往左取(看第3个print结果),由于索引号6与索引号-1指向的是同1个元素,你能猜出最后一条代码的运行结果吗?请参考文章末尾③

面试题解析

最后我们来推导一下面试题, a = [1,2,3,4,5,6],根据上面结果a[:]表示的是全列表元素

a = [1, 2, 3, 4, 5, 6]
print(a[:])
# [1, 2, 3, 4, 5, 6]
print(a[::1])
# [1, 2, 3, 4, 5, 6]
print(a[::-1])
# [6, 5, 4, 3, 2, 1]

由代码可知a[::1]因为步长为正的全列表故从左向右依次输出,而a[::-1]因为步长为负的全列表,故从右往左输出

关于上述面试题的解析到此为止,但Python关于切片的内容博大精深,此文仅是管中窥豹,其他内容请关注后续博文更新,此文原创,若有引用请注明出处,否则保留追究法律责任的权利!

①[‘腓力’, ‘多马’, ‘马太’]
②[‘西门彼得’, ‘安得烈’, ‘雅各布’, ‘约翰’, ‘腓力’, ‘多马’, ‘马太’]
③[‘马太’, ‘腓力’, ‘雅各布’]

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值