通过源码分析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实现sublime3的less编译插件示例
Apr 27 Python
Python引用(import)文件夹下的py文件的方法
Aug 26 Python
Python可跨平台实现获取按键的方法
Mar 05 Python
python实现的多线程端口扫描功能示例
Jan 21 Python
在NumPy中创建空数组/矩阵的方法
Jun 15 Python
pandas读取csv文件,分隔符参数sep的实例
Dec 12 Python
python 实现Flask中返回图片流给前端展示
Jan 09 Python
python常用运维脚本实例小结
Feb 14 Python
python内打印变量之%和f的实例
Feb 19 Python
Python logging模块进行封装实现原理解析
Aug 07 Python
python编程的核心知识点总结
Feb 08 Python
Python必备技巧之字符数据操作详解
Mar 23 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
用Simple Excel导出xls实现方法
2012/12/06 PHP
PHP命名空间(Namespace)的使用详解
2013/05/04 PHP
解析PHP无限级分类方法及代码
2013/06/21 PHP
PHP与jquery实时显示网站在线人数实例详解
2016/12/02 PHP
php中简单的对称加密算法实现
2017/01/05 PHP
如何打开php的gd2库
2017/02/09 PHP
php菜单/评论数据递归分级算法的实现方法
2019/08/01 PHP
jQuery学习7 操作JavaScript对象和集合的函数
2010/02/07 Javascript
js浮动图片的动态效果
2013/07/10 Javascript
javascript中错误使用var造成undefined
2016/03/31 Javascript
基于jQuery的select下拉框选择触发事件实例分析
2016/11/18 Javascript
Angular1.x自定义指令实例详解
2017/03/01 Javascript
几种响应式文字详解
2017/05/19 Javascript
使用layui 渲染table数据表格的实例代码
2018/08/19 Javascript
详解swiper在vue中的应用(以3.0为例)
2018/09/20 Javascript
JS实现textarea通过换行或者回车把多行数字分割成数组并且去掉数组中空的值
2018/10/29 Javascript
基于jquery实现九宫格拼图小游戏
2018/11/30 jQuery
Vue.js中 v-model 指令的修饰符详解
2018/12/03 Javascript
Vue-CLI项目中路由传参的方式详解
2019/09/01 Javascript
5分钟教你用nodeJS手写一个mock数据服务器的方法
2019/09/10 NodeJs
[00:03]DOTA2新版本PA至宝展示
2014/11/19 DOTA
用python读写excel的方法
2014/11/18 Python
python使用in操作符时元组和数组的区别分析
2015/05/19 Python
python检查指定文件是否存在的方法
2015/07/06 Python
python编写朴素贝叶斯用于文本分类
2017/12/21 Python
Django1.9 加载通过ImageField上传的图片方法
2018/05/25 Python
matplotlib调整子图间距,调整整体空白的方法
2018/08/03 Python
python ffmpeg任意提取视频帧的方法
2020/02/21 Python
喝酒检查书范文
2014/02/23 职场文书
中文专业求职信
2014/06/20 职场文书
语文课外活动总结
2014/08/27 职场文书
工艺技术员岗位职责
2015/02/04 职场文书
2015年药品销售工作总结范文
2015/05/25 职场文书
遗嘱格式范本
2015/08/07 职场文书
利用Selenium添加cookie实现自动登录的示例代码(fofa)
2021/05/08 Python
dubbo服务整合zipkin详解
2021/07/26 Java/Android