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


Posted in Python onMay 08, 2017

本文主要介绍的关于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 相关文章推荐
python获取标准北京时间的方法
Mar 24 Python
python实现随机森林random forest的原理及方法
Dec 21 Python
Python 3.x 安装opencv+opencv_contrib的操作方法
Apr 02 Python
python算法与数据结构之冒泡排序实例详解
Jun 22 Python
Pytorch 抽取vgg各层并进行定制化处理的方法
Aug 20 Python
使用celery和Django处理异步任务的流程分析
Feb 19 Python
python 的numpy库中的mean()函数用法介绍
Mar 03 Python
Python中无限循环需要什么条件
May 27 Python
Python web如何在IIS发布应用过程解析
May 27 Python
python给list排序的简单方法
Dec 10 Python
使用python对excel表格处理的一些小功能
Jan 25 Python
使用pd.merge表连接出现多余行的问题解决
Jun 16 Python
Python对文件和目录进行操作的方法(file对象/os/os.path/shutil 模块)
May 08 #Python
Python实现Windows和Linux之间互相传输文件(文件夹)的方法
May 08 #Python
Python实现SSH远程登陆,并执行命令的方法(分享)
May 08 #Python
利用Celery实现Django博客PV统计功能详解
May 08 #Python
浅谈Python生成器generator之next和send的运行流程(详解)
May 08 #Python
python生成式的send()方法(详解)
May 08 #Python
python实时分析日志的一个小脚本分享
May 07 #Python
You might like
从网上搜到的phpwind 0day的代码
2006/12/07 PHP
main.php
2006/12/09 PHP
PHP读取数据库并按照中文名称进行排序实现代码
2013/01/29 PHP
JavaScipt中的Math.ceil() 、Math.floor() 、Math.round() 三个函数的理解
2010/04/29 Javascript
javascript实现数字+字母验证码的简单实例
2014/02/10 Javascript
javascript(js)的小数点乘法除法问题详解
2014/03/07 Javascript
JS比较2个日期间隔的示例代码
2014/04/15 Javascript
easyui Draggable组件实现拖动效果
2015/08/19 Javascript
使用plupload自定义参数实现多文件上传
2016/07/19 Javascript
JS克隆,属性,数组,对象,函数实例分析
2016/11/26 Javascript
jQuery简单获取DIV和A标签元素位置的方法
2017/02/07 Javascript
详解js的异步编程技术的方法
2017/02/09 Javascript
AngularJS实现的2048小游戏功能【附源码下载】
2018/01/03 Javascript
react native基于FlatList下拉刷新上拉加载实现代码示例
2018/09/30 Javascript
微信小程序如何修改本地缓存key中单个数据的详解
2019/04/26 Javascript
亲自动手实现vue日历控件
2019/06/26 Javascript
Python3基础之函数用法
2014/08/13 Python
Python的网络编程库Gevent的安装及使用技巧
2016/06/24 Python
Python3中在Anaconda环境下安装basemap包
2018/10/21 Python
python实现Flappy Bird源码
2018/12/24 Python
Pyqt QImage 与 np array 转换方法
2019/06/27 Python
python格式化输出保留2位小数的实现方法
2019/07/02 Python
浅谈python图片处理Image和skimage的区别
2019/08/04 Python
python实现的爬取电影下载链接功能示例
2019/08/26 Python
python 画3维轨迹图并进行比较的实例
2019/12/06 Python
Python while循环使用else语句代码实例
2020/02/07 Python
学点简单的Django之第一个Django程序的实现
2021/02/24 Python
浅析Python模块之间的相互引用问题
2021/02/26 Python
德尔福集团DELPHI的笔试题
2012/02/22 面试题
应届毕业生应聘自荐信
2013/12/07 职场文书
大学生个人自荐信
2014/02/24 职场文书
2014年帮扶工作总结
2014/11/26 职场文书
2014幼儿园班主任工作总结
2014/12/04 职场文书
销售员岗位职责范本
2015/04/11 职场文书
干货:如何写好观后感 !
2019/05/21 职场文书
CSS实现隐藏搜索框功能(动画正反向序列)
2021/07/21 HTML / CSS