# 切片的实现
# list的切片实现
在 Python 中,列表对象的切片是通过内置的 slice() 函数实现的。slice() 函数的调用方式如下所示:
slice(start, stop[, step])
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
print(l[::-1])
# 等价于
s = slice(None, None, -1)
print(l[s])
其中,start 表示切片开始的位置,stop 表示切片结束的位置,step 表示切片的步长。如果 start 和 step 都没有给出,则默认为 0 和 1。
当进行切片操作时,Python 会创建一个新的列表对象,然后从原来的列表对象中复制出一段数据,然后将其赋值给新的列表对象。具体来说,切片的实现过程如下:
- 首先,根据
start、stop 和step 参数计算出切片的起始位置、终止位置和步长。 - 然后,创建一个新的列表对象,用来存储切片后的数据。
- 接着,根据起始位置、终止位置和步长,从原来的列表对象中复制出一段数据。
- 最后,将复制出来的数据赋值给新的列表对象。
需要注意的是,切片操作只是复制了原列表的一部分元素,而并没有改变原列表本身。因此,如果对新列表进行修改操作,不会影响原列表。
这个复制是很有趣的,因为python中一切皆对象,所以实际上复制过去的是指向对象的指针,所以对象可变则实际上还是会同步改变,对象不可变就会看着像不同步改变。
但是!仔细考虑,即使对象可变,可变的是其内部状态,其本身是没有任何变化的,即id(object)是没有变化的,所以说其不同步改变是没有问题的。
l = [[1],[2],[3]] sl = l[::2] print(sl) # [[1], [3]] for p in sl: p.append(0) print(sl) # [[1, 0], [3, 0]] print(l) # [[1, 0], [2], [3, 0]] sl[1] = "10086" print(sl) # [[1, 0], '10086'] print(l) # [[1, 0], [2], [3, 0]]
l = list(range(20))
l_reversed = l[::-1]
l_reversed[0] = -1
print(l) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
print(l_reversed) # [-1, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# 多维切片和...
实际上python的切片操作时支持多维切片的,当进行多维切片时,即使用类似l[1:2,2:3,3:4]的语法时,实际上__getitem__接收到的index是一个slice的元组。
同时注意Python在切片中存在...的写法,其看上去虽然是省略号(注意实际写法是三个英文句号,而不是输入法打出来的省略号),但实际上是eclipse类的唯一实例Ellipsis的别名,其是切片规范的一部分,用户需要自己处理Ellipsis-Python内建数据类型都只支持一维切片,因此没有Ellipsis用武之地。
l[1:2,2:3,3:4] # {'index': (slice(1, 2, None), slice(2, 3, None), slice(3, 4, None))}
l[1:2,...,3:4] # {'index': (slice(1, 2, None), Ellipsis, slice(3, 4, None))}
l[1:2,...,3:4,...] # {'index': (slice(1, 2, None), Ellipsis, slice(3, 4, None), Ellipsis)}
l[1:...] # {'index': slice(1, Ellipsis, None)}
# 自定义数据结构实现切片
# 切片操作
class MyList(object):
def __init__(self):
self.mlist = list()
def __getitem__(self, index):
print('__getitem__() called ', locals())
if isinstance(index, slice):
return self.mlist[index]
def __setitem__(self, index, value):
print('__getitem__() called ', locals())
if isinstance(index, slice):
self.mlist[index] = value
def __delitem__(self, index):
print('__delitem__() called ', locals())
if isinstance(index, slice):
del self.mlist[index]
l = MyList()
l[0] # {'index': 0}
l[::-1] # {'index': slice(None, None, -1)}
l[0:10:1] # {'index': slice(0, 10, 1)}
l[1:2,2:3,3:4] # {'index': (slice(1, 2, None), slice(2, 3, None), slice(3, 4, None))}
l[1:2,...,3:4] # {'index': (slice(1, 2, None), Ellipsis, slice(3, 4, None))}
l[1:2,...,3:4,...] # {'index': (slice(1, 2, None), Ellipsis, slice(3, 4, None), Ellipsis)}
l[1:...] # {'index': slice(1, Ellipsis, None)}