通过源码分析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使用random和tertools模块解一些经典概率问题
Jan 28 Python
Python基于贪心算法解决背包问题示例
Nov 27 Python
Python使用Windows API创建窗口示例【基于win32gui模块】
May 09 Python
对python中使用requests模块参数编码的不同处理方法
May 18 Python
使用Python实现企业微信的自动打卡功能
Apr 30 Python
pandas数据筛选和csv操作的实现方法
Jul 02 Python
pytorch 在网络中添加可训练参数,修改预训练权重文件的方法
Aug 17 Python
Python中的延迟绑定原理详解
Oct 11 Python
Python基于WordCloud制作词云图
Nov 29 Python
Python如何使用函数做字典的值
Nov 30 Python
解决Pycharm 运行后没有输出的问题
Feb 05 Python
Python中tqdm的使用和例子
Sep 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
咖啡店都有些什么常规豆子呢?有什么风味在里面
2021/03/04 咖啡文化
PHP产生不重复随机数的5个方法总结
2014/11/12 PHP
在WordPress中使用wp_count_posts函数来统计文章数量
2016/01/05 PHP
Laravel 实现数据软删除功能
2019/08/21 PHP
JavaScript 替换Html标签实现代码
2009/10/14 Javascript
jQuery 动画弹出窗体支持多种展现方式
2010/04/29 Javascript
jquery得到font-size属性值实现代码
2013/09/30 Javascript
js的for in循环和java里foreach循环的区别分析
2015/01/28 Javascript
Jquery树插件zTree用法入门教程
2015/02/17 Javascript
javascript表格的渲染组件
2015/07/03 Javascript
下雪了 javascript实现雪花飞舞
2020/08/02 Javascript
JS继承之借用构造函数继承和组合继承
2016/09/07 Javascript
JS获取html元素的标记名实现方法
2016/10/08 Javascript
浅谈express 中间件机制及实现原理
2017/08/31 Javascript
详解vue.js数据传递以及数据分发slot
2018/01/20 Javascript
使用mpvue搭建一个初始小程序及项目配置方法
2018/12/03 Javascript
React-redux实现小案例(todolist)的过程
2019/09/29 Javascript
适合前端Vue开发童鞋的跨平台Weex的使用详解
2019/10/16 Javascript
javascript前端和后台进行数据交互方法示例
2020/08/07 Javascript
python 基础教程之Map使用方法
2017/01/17 Python
详解Python匿名函数(lambda函数)
2019/04/19 Python
Python实现语音识别和语音合成功能
2019/09/20 Python
Django继承自带user表并重写的例子
2019/11/18 Python
Python优秀开源项目Rich源码解析的流程分析
2020/07/06 Python
Python collections.deque双边队列原理详解
2020/10/05 Python
python 装饰器重要在哪
2021/02/14 Python
python爬虫beautifulsoup库使用操作教程全解(python爬虫基础入门)
2021/02/19 Python
接口中的方法可以是abstract的吗
2015/07/23 面试题
劳动模范事迹材料
2014/01/19 职场文书
如何编写优秀的食品项目创业计划书
2014/01/23 职场文书
国际贸易毕业生求职信范文
2014/02/21 职场文书
课堂教学改革实施方案
2014/03/17 职场文书
纪念九一八爱国演讲稿600字
2014/09/14 职场文书
村长反四风问题个人对照检查材料
2014/09/21 职场文书
六一儿童节标语
2014/10/08 职场文书
2015年全国保险公众宣传日活动方案
2015/05/06 职场文书