通过源码分析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 爬取微信文章
Jan 30 Python
离线安装Pyecharts的步骤以及依赖包流程
Apr 23 Python
Python+Wordpress制作小说站
Apr 14 Python
python简单线程和协程学习心得(分享)
Jun 14 Python
python中的tcp示例详解
Dec 09 Python
新手入门Python编程的8个实用建议
Jul 12 Python
Python+numpy实现矩阵的行列扩展方式
Nov 29 Python
pytorch如何冻结某层参数的实现
Jan 10 Python
pytorch-RNN进行回归曲线预测方式
Jan 14 Python
pip安装tensorflow的坑的解决
Apr 19 Python
python实现二分查找算法
Sep 18 Python
PYTHON基于Pyecharts绘制常见的直角坐标系图表
Apr 28 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 获取本地IP代码
2013/06/23 PHP
ini_set的用法介绍
2014/01/07 PHP
smarty内置函数capture用法分析
2015/01/22 PHP
jQuery chili图片远处放大插件
2009/11/30 Javascript
Web 前端设计模式--Dom重构 提高显示性能
2010/10/22 Javascript
使用CSS样式position:fixed水平滚动的方法
2014/02/19 Javascript
javascript(js)的小数点乘法除法问题详解
2014/03/07 Javascript
jQuery+slidereveal实现的面板滑动侧边展出效果
2015/03/14 Javascript
javascript中new关键字详解
2015/12/14 Javascript
浅谈JavaScript 覆盖原型以及更改原型
2016/08/31 Javascript
无循环 JavaScript(map、reduce、filter和find)
2017/04/08 Javascript
React如何解决fetch跨域请求时session失效问题
2018/11/02 Javascript
初探Vue3.0 中的一大亮点Proxy的使用
2018/12/06 Javascript
基于Koa(nodejs框架)对json文件进行增删改查的示例代码
2019/02/02 NodeJs
vue子传父关于.sync与$emit的实现
2019/11/05 Javascript
ReactRouter的实现方法
2021/01/25 Javascript
[02:07]DOTA2新英雄展现中国元素,完美“圣典”亮相央视
2016/12/19 DOTA
python调用Delphi写的Dll代码示例
2017/12/05 Python
Python pymongo模块常用操作分析
2018/09/01 Python
django session完成状态保持的方法
2018/11/27 Python
Python使用修饰器进行异常日志记录操作示例
2019/03/19 Python
Django如何实现网站注册用户邮箱验证功能
2019/08/14 Python
python爬虫 requests-html的使用
2020/11/30 Python
你可能不熟练的十个前端HTML5经典面试题
2018/07/03 HTML / CSS
澳大利亚领先的睡衣品牌:Peter Alexander
2016/08/16 全球购物
ESDlife健康生活易:身体检查预订、搜寻及比较
2019/05/10 全球购物
bonprix荷兰网上商店:便宜的服装、鞋子和家居用品
2020/07/04 全球购物
几道数据库的面试题或笔试题
2014/05/31 面试题
历史专业学生的自我评价
2014/02/28 职场文书
初一学生期末评语
2014/04/24 职场文书
银行纠风工作实施方案
2014/06/08 职场文书
师德师风剖析材料
2014/09/30 职场文书
车间质检员岗位职责
2015/04/08 职场文书
人与自然观后感
2015/06/16 职场文书
社区挂职锻炼个人工作总结
2015/10/23 职场文书
JavaScript 数组去重详解
2021/09/15 Javascript