通过源码分析Python中的切片赋值

yipeiwu_com6年前Python基础

本文主要介绍的关于Python切片赋值的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍:

昨天有同学问了我这么个问题:

t = [1, 2, 3]
t[1:1] = [7] # 感谢@一往直前 的疑问,之前写为 t[1:1] = 7了
print t # 输出 [1, 7, 2, 3]

这个问题之前还真没遇到过,有谁会对列表这么进行赋值吗?不过对于这个输出结果的原因确实值得去再了解下,毕竟之前也看过《Python源码分析》。(题外话:据说最近有大牛在写新的版本)

想着今天有空看看Python的源码,去了解下原理是什么。

注:我本地之前下载的是Python2.7.6的代码,直接看的这个。

在Objects/listobject.c中有一个 PyList_SetSlice 函数,是这么写的:

int
PyList_SetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
{
 if (!PyList_Check(a)) {
  PyErr_BadInternalCall();
  return -1;
 }
 return list_ass_slice((PyListObject *)a, ilow, ihigh, v);
}

有用的一句就是 list_ass_slice ,那么再来看看这个函数的代码:

static int
list_ass_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
{
 /* Because [X]DECREF can recursively invoke list operations on
 this list, we must postpone all [X]DECREF activity until
 after the list is back in its canonical shape. Therefore
 we must allocate an additional array, 'recycle', into which
 we temporarily copy the items that are deleted from the
 list. :-( */
 PyObject *recycle_on_stack[8];
 PyObject **recycle = recycle_on_stack; /* will allocate more if needed */
 PyObject **item;
 PyObject **vitem = NULL;
 PyObject *v_as_SF = NULL; /* PySequence_Fast(v) */
 Py_ssize_t n; /* # of elements in replacement list */
 Py_ssize_t norig; /* # of elements in list getting replaced */
 Py_ssize_t d; /* Change in size */
 Py_ssize_t k;
 size_t s;
 int result = -1;   /* guilty until proved innocent */
#define b ((PyListObject *)v)
 if (v == NULL)
  n = 0;
 else {
  if (a == b) {
   /* Special case "a[i:j] = a" -- copy b first */
   v = list_slice(b, 0, Py_SIZE(b));
   if (v == NULL)
    return result;
   result = list_ass_slice(a, ilow, ihigh, v);
   Py_DECREF(v);
   return result;
  }
  v_as_SF = PySequence_Fast(v, "can only assign an iterable");
  if(v_as_SF == NULL)
   goto Error;
  /*
  the5fire注:
  要赋值的长度n
  */
  n = PySequence_Fast_GET_SIZE(v_as_SF);
  vitem = PySequence_Fast_ITEMS(v_as_SF);
 }
 if (ilow < 0)
  ilow = 0;
 else if (ilow > Py_SIZE(a))
  ilow = Py_SIZE(a);

 if (ihigh < ilow)
  ihigh = ilow;
 else if (ihigh > Py_SIZE(a))
  ihigh = Py_SIZE(a);

 norig = ihigh - ilow;
 assert(norig >= 0);
 d = n - norig;
 if (Py_SIZE(a) + d == 0) {
  Py_XDECREF(v_as_SF);
  return list_clear(a);
 }
 item = a->ob_item;
 /* recycle the items that we are about to remove */
 s = norig * sizeof(PyObject *);
 if (s > sizeof(recycle_on_stack)) {
  recycle = (PyObject **)PyMem_MALLOC(s);
  if (recycle == NULL) {
   PyErr_NoMemory();
   goto Error;
  }
 }
 memcpy(recycle, &item[ilow], s);

 if (d < 0) { /* Delete -d items */
  memmove(&item[ihigh+d], &item[ihigh],
   (Py_SIZE(a) - ihigh)*sizeof(PyObject *));
  list_resize(a, Py_SIZE(a) + d);
  item = a->ob_item;
 }
 else if (d > 0) { /* Insert d items */
  k = Py_SIZE(a);
  if (list_resize(a, k+d) < 0)
   goto Error;
  item = a->ob_item;
  printf("关键点\n");
  /*
  the5fire注:
  把list对应切片后一位的值之后的所有内容向后移动所赋值的大小
  按照上面的python代码这里就是
  原理的t:
  |1|2|3|
  后移一位,因为len([7]) = 1
  |1|空|2|3|把后两个移位
  */
  memmove(&item[ihigh+d], &item[ihigh],
   (k - ihigh)*sizeof(PyObject *));
 }
 /*
 the5fire注:
 赋值操作,即把[7]赋值到t里的对应位置上
 ilow是1, n是1
 */
 for (k = 0; k < n; k++, ilow++) {
  PyObject *w = vitem[k];
  Py_XINCREF(w);
  item[ilow] = w;
 }
 for (k = norig - 1; k >= 0; --k)
  Py_XDECREF(recycle[k]);
 result = 0;
Error:
 if (recycle != recycle_on_stack)
  PyMem_FREE(recycle);
 Py_XDECREF(v_as_SF);
 return result;
#undef b
}

看了知乎,stackoverflow上的解答,发现源码还是最好的解释。上述关键位置已经加了注释,应该很好理解。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对【听图阁-专注于Python设计】的支持。

相关文章

PowerBI和Python关于数据分析的对比

PowerBI和Python关于数据分析的对比

前言 如果你对数据分析有一定的了解,那你一定听说过一些亲民好用的数据分析的工具,如Excel、Tableau、PowerBI等等等等,它们都是数据分析的得力助手。像经常使用这些根据的伙伴...

Python中使用ElementTree解析XML示例

【XML基本概念介绍】 XML 指可扩展标记语言(eXtensible Markup Language)。 XML 被设计用来传输和存储数据。 概念一: 复制代码 代码如下: <...

python中bisect模块用法实例

本文实例讲述了python中bisect模块用法,分享给大家供大家参考。 具体方法分析如下: 这个模块只有几个函数,一旦决定使用二分搜索时,立马要想到使用这个模块。 示例代码如下:...

Ubuntu下Anaconda和Pycharm配置方法详解

Ubuntu下Anaconda和Pycharm配置方法详解

本文为大家分享了Ubuntu下Anaconda和Pycharm的配置方法,供大家参考,具体内容如下 1.对于Ubuntu18.04,一开始会有一个系统默认的python解释器,是3.6版...

python学习之编写查询ip程序

python学习之编写查询ip程序

公司服务器上的ip最少的也有100多个,有时候查到一个站的Ip, 不想通过OA去查,自己就用自己最近学的python知识,结合数据库,编写了一python小程序。实现只要输入主ip就能查...