深入理解python中的切片

深入理解python中的切片,第1张

切片的基本使用
sequence[start:stop:step]

start:切片的起始索引,它将包括此索引处的元素,除非它与 stop 相同,默认为 0,即第一个索引。
stop:切片的结束索引,它不包括此索引处的元素,默认为被切片序列的长度,即直到并包括结束。
step:步长,默认为 1。如果它是负数,则从后往前进行切片。

省略开始索引时,默认值为 0,省略结束索引时,默认为到字符串的结尾
同样支持负数的索引,标号从最后一个元素往前依次为-1,-2,…
根据步长的正负的不同,切片表示将有所差异:

[a:b:c]
len = length of string, tuple or list
c – default is +1. The sign of c indicates forward or backward, absolute value of c indicates steps. Default is forward with step size 1. Positive means forward, negative means backward.
a – When c is positive or blank, default is 0. When c is negative, default is -1.
b – When c is positive or blank, default is len. When c is negative, default is -(len+1).

when step is positive

>>> seq[:]                # [seq[0],   seq[1],          ..., seq[-1]    ]
>>> seq[low:]             # [seq[low], seq[low+1],      ..., seq[-1]    ]
>>> seq[:high]            # [seq[0],   seq[1],          ..., seq[high-1]]
>>> seq[low:high]         # [seq[low], seq[low+1],      ..., seq[high-1]]
>>> seq[::stride]         # [seq[0],   seq[stride],     ..., seq[-1]    ]
>>> seq[low::stride]      # [seq[low], seq[low+stride], ..., seq[-1]    ]
>>> seq[:high:stride]     # [seq[0],   seq[stride],     ..., seq[high-1]]
>>> seq[low:high:stride]  # [seq[low], seq[low+stride], ..., seq[high-1]]
lst = [1,2,3,4,5]
lst[1:] #>>[2,3,4,5]
lst[:4] #>>[1, 2, 3, 4]
lst[-4:4:2]#>>[2,4]   #从倒数第四个到第四个元素,步长为2

when step is negative

>>> seq[::-stride]        # [seq[-1],   seq[-1-stride],   ..., seq[0]    ]
>>> seq[high::-stride]    # [seq[high], seq[high-stride], ..., seq[0]    ]
>>> seq[:low:-stride]     # [seq[-1],   seq[-1-stride],   ..., seq[low+1]]
>>> seq[high:low:-stride] # [seq[high], seq[high-stride], ..., seq[low+1]]
lst = [1,2,3,4,5]
lst[::-1]  #>>[5, 4, 3, 2, 1]
lst[-3::-1]  #>>[3, 2, 1]
lst[-2:1:-1] #>>[4, 3]  从倒数第二个到第三个元素

并且相比索引,索引越界会报错,而切片会自动处理越界索引

lst = [0,1,2,3,4]
lst[3:9] #>>[3,4]
lst[9:] #>>[]

今天在回顾切片这个知识点的时候,又明确了一个概念就是对列表的切片其实都是返回一个新的浅拷贝对象。

a = [1, [0,2], 3]
b = a[:2]
a[1][0] = 4
b  #>>[1, [4, 2]]

进而对整个列表的浅拷贝(复制列表)即为

a = [1, [0,2], 3]
b = a[:]
b #>>[1, [0, 2], 3]

明确了切片是返回一个新的对象后,产生了一个疑问也是在stackflow中看到了一样的问题即

a = [1, 2, 3]
a[0:2] = [4, 5]
print(a)
# 输出
# [4, 5, 3] 

为什么这里a[0:2]可以修改原始列表,它不是应该是新的列表对象吗?

该问题下的一个回答:
I came across the same question before and it’s related to the language specification. According to assignment-statements,

  1. If the left side of assignment is subscription, Python will call __setitem__ on that object.a[i] = x is equivalent to .a.__setitem__(i, x)
  2. If the left side of assignment is slice, Python will also call __setitem__, but with different arguments: a[1:4]=[1,2,3]is equivalent to a.__setitem__(slice(1,4,None), [1,2,3])
    That’s why list slice on the left side of ‘=’ behaves differently.

大致意思就是python中切片出现在赋值符号 = 的左边行为会发生改变,在左边调用__setitem__ 方法,而在 = 的右边则是调用__getitem__方法

python官方文档给出了解释
如果目标为一个切片:引用中的原型表达式会被求值。 它应当产生一个可变序列对象(例如列表)。 被赋值对象应当是一个相同类型的序列对象。 接下来,下界与上界表达式如果存在的话将被求值;默认值分别为零和序列长度。 上下边界值应当为整数。 如果某一边界为负值,则会加上序列长度。 求出的边界会被裁剪至介于零和序列长度的开区间中。 最后,将要求序列对象将切片替换为指定序列的项目。 切片的长度可能与指定序列的长度不同,这会在目标序列允许的情况下改变目标序列的长度。(目标即=左边,被赋值对象即=右边)

解释一下就是切片出现在 = 左边时,并没有创建一个新的对象而是将这个被赋值后的切片插入到原始的序列对象当中,并且允许被赋值对象的序列长度大于切片的长度

a = [1, 2, 3]
a[0:2] = [4,5,6,7]
print(a)
# 输出
# [4, 5, 6, 7, 3]

但还需注意的一点是我们还可以改变空切片,相当于在该位置中插入元素

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

同样python官方文档也解释了 del a[:]的原理

属性引用、抽取和切片的删除会被传递给相应的原型对象;删除一个切片基本等价于赋值为一个右侧类型的空切片(但即便这一点也是由切片对象决定的)。
del a[:]等价于a[:] = []


参考:
https://stackoverflow.com/questions/10623302/how-does-assignment-work-with-list-slices
https://stackoverflow.com/questions/509211/understanding-slicing
https://docs.python.org/zh-cn/3/reference/simple_stmts.html#the-del-statement

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/868381.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存