古明地觉的编程教室

深度解密 Python 切片的实现原理

Image

楔子

本篇文章来探讨一下切片是如何实现的,因为在操作字符串、元组、列表等数据结构时,我们经常会使用切片截取数据,所以对切片做一个全方位的了解是很有必要的。

data = list(range(10))
print(data[1: 8: 3])  # [1, 4, 7]

以上就是基于切片截取数据,在工作中我们会大量使用切片。但你知道吗,其实切片也是一个对象,类型为 slice,下面我们来看一下切片的底层结构。

Image

切片的底层结构

切片的类型是 slice,那么根据解释器的 API 命名规则,我们猜测:

  • 切片(slice 对象)在底层对应 PySliceObject 结构体实例;

  • slice 类型本身在底层对应 PySlice_Type;

下面看一下具体实现。

// Include/sliceobject.h
typedef struct {
    PyObject_HEAD
    PyObject *start, *stop, *step;
} PySliceObject;

切片是不可变对象,除了对象的公共头部之外,还有三个字段,分别表示切片的起始位置、终止位置、步长。这也意味着创建切片时,可以给 slice 传递三个参数。

# 创建一个切片
s = slice(1, 8, 3)
print(s)  # slice(1, 8, 3)

问题来了,切片创建的时候,传递的参数绑定在了哪些属性上呢?前面我们说过,实例对象可以绑定哪些属性,会定义在类型对象的 tp_members 字段中。

Image

我们看到切片拥有三个属性,名称也是 start、stop、step。

s = slice(1, 8, 3)
print(s)  # slice(1, 8, 3)
print(s.start)  # 1
print(s.stop)  # 8
print(s.step)  # 3

非常简单,你在 Python 里面看到的一切,都能从源码中找到答案。

Image

切片是怎么创建的

切片是内置类型的实例对象,对于这样的对象,有两种创建方式,相信你已经知道我要说什么了。我们在最开始专门用了十篇文章,从宏观的角度介绍了 Python 的对象模型,目的就在于此。