通过源码分析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的Django框架下管理站点的基本方法
Jul 17 Python
python黑魔法之参数传递
Feb 12 Python
Python的Twisted框架中使用Deferred对象来管理回调函数
May 25 Python
python实现图书管理系统
Mar 12 Python
Python决策树之基于信息增益的特征选择示例
Jun 25 Python
python 反向输出字符串的方法
Jul 16 Python
对python_discover方法遍历所有执行的用例详解
Feb 13 Python
nginx黑名单和django限速,最简单的防恶意请求方法分享
Aug 09 Python
Python hmac模块使用实例解析
Dec 24 Python
Python函数的返回值、匿名函数lambda、filter函数、map函数、reduce函数用法实例分析
Dec 26 Python
浅谈Python中os模块及shutil模块的常规操作
Apr 03 Python
python让函数不返回结果的方法
Jun 22 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
使用PHP+MySql实现微信投票功能实例代码
2017/09/29 PHP
JS 显示当前日期与时间的代码
2010/03/24 Javascript
js实现GridView单选效果自动设置交替行、选中行、鼠标移动行背景色
2010/05/27 Javascript
Javascript操作cookie的函数代码
2012/10/03 Javascript
jQuery 选择表格(table)里的行和列及改变简单样式
2012/12/15 Javascript
js和as的稳定传值问题解决
2013/07/14 Javascript
JavaScript中的函数重载深入理解
2014/08/04 Javascript
jQuery中prepend()方法用法实例
2014/12/25 Javascript
使用jquery 简单实现下拉菜单
2015/01/14 Javascript
Jquery遍历Json数据的方法
2015/04/20 Javascript
深入理解js函数的作用域与this指向
2016/05/28 Javascript
基于JS代码实现导航条弹出式悬浮菜单
2016/06/17 Javascript
AngularJS 依赖注入详解和简单实例
2016/07/28 Javascript
Google 地图API Map()构造器详解
2016/08/06 Javascript
Bootstrap Modal对话框如何在关闭时触发事件
2016/12/02 Javascript
JavaScript基本类型值-Undefined、Null、Boolean
2017/02/23 Javascript
Angular.js中处理页面闪烁的方法详解
2017/03/09 Javascript
微信小程序 实现列表项滑动显示删除按钮的功能
2017/04/13 Javascript
浅谈js中的this问题
2017/08/31 Javascript
Vue 页面切换效果之 BubbleTransition(推荐)
2018/04/08 Javascript
vue组件通信传值操作示例
2019/01/08 Javascript
浅谈ECMAScript 中的Array类型
2019/06/10 Javascript
在layui中对table中的数据进行判断(0、1)转换为提示信息的方法
2019/09/28 Javascript
vue实现表格合并功能
2020/12/01 Vue.js
[03:56]还原FTP电影首映式 DOTA2群星拼出遗迹世界
2014/03/26 DOTA
python使用自定义user-agent抓取网页的方法
2015/04/15 Python
VSCode下好用的Python插件及配置
2018/04/06 Python
DjangoWeb使用Datatable进行后端分页的实现
2020/05/18 Python
python如何实现图片压缩
2020/09/11 Python
Python基于unittest实现测试用例执行
2020/11/25 Python
CSS3 transforms应用于背景图像的解决方法
2019/04/16 HTML / CSS
安德玛加拿大官网:Under Armour加拿大
2019/10/02 全球购物
青岛海底世界导游词
2015/02/11 职场文书
高中升旗仪式主持词
2015/07/03 职场文书
七年级话题作文之执着
2019/11/19 职场文书
关于vue中如何监听数组变化
2021/04/28 Vue.js