Lin_R

## 前言

sequence[ilow:ihigh:step]  # ihigh,step 可为空; 为了简短易懂, 暂时排除step的用法考虑

``````sequence = [1,2,3,4,5]
sequence [ilow:ihigh]  # 从ilow开始到ihigh-1结束
sequence [ilow:]       # 从ilow开始直到末尾
sequence [:ihigh]      # 从头部开始直到ihigh结束
sequence [:]           # 复制整个列表``````

1. ilow, ihigh均小于 sequece的长度
2. ilow < ihigh

``````sequence = [1,2,3,4,5]
print sequence[1]   # 输出2
print sequence[2]   # 输出3
``````

``````sequence = [1,2,3,4,5]
print sequence[15]

### 输出 ###
Traceback (most recent call last):
File "test.py", line 2, in <module>
print a[20]
IndexError: list index out of range``````

## 情景重现

``````# version: python2.7

a = [1, 2, 3, 5]
print a[10:20]  # 结果会报异常吗?``````

``````>>> a = [1, 2, 3, 5]
>>> print a[10:20]
[]``````

``````>>> s = '23123123123'
>>> print s[400:2000]

>>> t = (1, 2, 3,4)
>>> print t[200: 1000]
()``````

## 原理分析

``````#############  切片 ################
[root@iZ23pynfq19Z ~]# cat test.py
a = [11,2,3,4]
print a[20:30]

#结果:
[root@iZ23pynfq19Z ~]# python -m dis test.py
1           0 LOAD_CONST               0 (11)
3 LOAD_CONST               1 (2)
6 LOAD_CONST               2 (3)
9 LOAD_CONST               3 (4)
12 BUILD_LIST               4
15 STORE_NAME               0 (a)

2          18 LOAD_NAME                0 (a)
21 LOAD_CONST               4 (20)
24 LOAD_CONST               5 (30)
27 SLICE+3
28 PRINT_ITEM
29 PRINT_NEWLINE
30 LOAD_CONST               6 (None)
33 RETURN_VALUE

#############  单下标取值 ################
[root@gitlab ~]# cat test2.py
a = [11,2,3,4]
print a[20]

#结果:
[root@gitlab ~]# python -m dis test2.py
1           0 LOAD_CONST               0 (11)
3 LOAD_CONST               1 (2)
6 LOAD_CONST               2 (3)
9 LOAD_CONST               3 (4)
12 BUILD_LIST               4
15 STORE_NAME               0 (a)

2          18 LOAD_NAME                0 (a)
21 LOAD_CONST               4 (20)
24 BINARY_SUBSCR
25 PRINT_ITEM
26 PRINT_NEWLINE
27 LOAD_CONST               5 (None)
30 RETURN_VALUE

``````

• 第一列是数字是原始源代码的行号。
• 第三列是字节码人类可读的名字。它们是为程序员所准备的
• 第四列表示指令的参数
• 第五列是计算后的实际参数

``````/*取自: python2.7 python/ceval.c */

// 第一步:
PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
{
....   // 省略n行代码
TARGET_WITH_IMPL_NOARG(SLICE, _slice)
TARGET_WITH_IMPL_NOARG(SLICE_1, _slice)
TARGET_WITH_IMPL_NOARG(SLICE_2, _slice)
TARGET_WITH_IMPL_NOARG(SLICE_3, _slice)
_slice:
{
if ((opcode-SLICE) & 2)
w = POP();
else
w = NULL;
if ((opcode-SLICE) & 1)
v = POP();
else
v = NULL;
u = TOP();
x = apply_slice(u, v, w);    // 取出v: ilow, w: ihigh, 然后调用apply_slice
Py_DECREF(u);
Py_XDECREF(v);
Py_XDECREF(w);
SET_TOP(x);
if (x != NULL) DISPATCH();
break;
}

....   // 省略n行代码
}

// 第二步:
apply_slice(PyObject *u, PyObject *v, PyObject *w) /* return u[v:w] */
{
PyTypeObject *tp = u->ob_type;
PySequenceMethods *sq = tp->tp_as_sequence;

if (sq && sq->sq_slice && ISINDEX(v) && ISINDEX(w)) { // v,w的类型检查,要整型/长整型对象
Py_ssize_t ilow = 0, ihigh = PY_SSIZE_T_MAX;
if (!_PyEval_SliceIndex(v, &ilow))                // 将v对象再做检查, 并将其值转换出来,存给ilow
return NULL;
if (!_PyEval_SliceIndex(w, &ihigh))               // 同上
return NULL;
return PySequence_GetSlice(u, ilow, ihigh);       // 获取u对象对应的切片函数
}
else {
PyObject *slice = PySlice_New(v, w, NULL);
if (slice != NULL) {
PyObject *res = PyObject_GetItem(u, slice);
Py_DECREF(slice);
return res;
}
else
return NULL;
}

// 第三步:
PySequence_GetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2)
{
PySequenceMethods *m;
PyMappingMethods *mp;

if (!s) return null_error();

m = s->ob_type->tp_as_sequence;
if (m && m->sq_slice) {
if (i1 < 0 || i2 < 0) {
if (m->sq_length) {
// 先做个简单的初始化, 如果左右下表小于, 将其加上sequence长度使其归为0
Py_ssize_t l = (*m->sq_length)(s);
if (l < 0)
return NULL;
if (i1 < 0)
i1 += l;
if (i2 < 0)
i2 += l;
}
}
// 真正调用对象的sq_slice函数, 来执行切片的操作
return m->sq_slice(s, i1, i2);
} else if ((mp = s->ob_type->tp_as_mapping) && mp->mp_subscript) {
PyObject *res;
PyObject *slice = _PySlice_FromIndices(i1, i2);
if (!slice)
return NULL;
res = mp->mp_subscript(s, slice);
Py_DECREF(slice);
return res;
}

return type_error("'%.200s' object is unsliceable", s);

``````

``````// 字符串对象
StringObject.c:  (ssizessizeargfunc)string_slice, /*sq_slice*/

// 列表对象
ListObject.c: (ssizessizeargfunc)list_slice,      /* sq_slice */

// 元组
TupleObject.c: (ssizessizeargfunc)tupleslice,     /* sq_slice */

``````

因为他们三个的函数实现大致相同, 所以我们只分析其中一个就可以了, 下面是对列表的切片函数分析:

``````/* 取自ListObject.c */
static PyObject *
list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
{
PyListObject *np;
PyObject **src, **dest;
Py_ssize_t i, len;
if (ilow < 0)
ilow = 0;
else if (ilow > Py_SIZE(a))               // 如果ilow大于a长度, 那么重新赋值为a的长度
ilow = Py_SIZE(a);
if (ihigh < ilow)
ihigh = ilow;
else if (ihigh > Py_SIZE(a))              // 如果ihigh大于a长度, 那么重新赋值为a的长度
ihigh = Py_SIZE(a);
len = ihigh - ilow;
np = (PyListObject *) PyList_New(len);    // 创建一个ihigh - ilow的新列表对象
if (np == NULL)
return NULL;

src = a->ob_item + ilow;
dest = np->ob_item;
for (i = 0; i < len; i++) {               // 将a处于该范围内的成员, 添加到新列表对象
PyObject *v = src[i];
Py_INCREF(v);
dest[i] = v;
}
return (PyObject *)np;
}
``````

## 结论

### Lin_R

wanyang_wanyang
2018/07/03
0
0
Python3基础知识的讲解（三）

2018/06/01
0
0
Python list列表---学习总结

PurpleKing
2018/08/04
0
0
Python 进阶：全面解读高级特性之切片！

CSDN资讯
03/20
0
0
Python开发（基础）：常用函数

enumerate 函数定义： def enumerate(sequence, start=0): n = start for elem in sequence: yield n, elem n += 1 函数示例： >>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']>>> li......

wbb827
2018/07/03
0
0

JavaConfig版

40分钟前
4
0
Go 定时器内部实现原理剖析

44分钟前
0
0

ZooKeeper 最早起源于雅虎研究院的一个研究小组。当时，雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调，但是这些系统往往都存在分布式单点问题。所以，雅虎的开发人员就...

45分钟前
1
0
Linux learn（三）

6. Linux文件与目录管理（续上一篇） 查看文件类型 file file 文件名 例如： 文件搜索 which(\$PATH查询执行档) 结构: which [-a] command 选项参数： -a: 将所有PATH目录中可以找到的指令均列...

lazy~
49分钟前
1
0

//二叉搜索树条件左子树<根<右子树 //后序遍历说明最后一个元素是该二叉树的根节点 //1 找到该树的左子树 //2 判断右子树是否都大于根的值 //3 同样操作，该根的左右子树是否成立 public cla...

51分钟前
1
0