通过源码分析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编程实现归并排序
Apr 14 Python
Python递归实现汉诺塔算法示例
Mar 19 Python
Python实现微信消息防撤回功能的实例代码
Apr 29 Python
python实现网站用户名密码自动登录功能
Aug 09 Python
在PyTorch中Tensor的查找和筛选例子
Aug 18 Python
Python中关于浮点数的冷知识
Sep 22 Python
Python3实现配置文件差异对比脚本
Nov 18 Python
解决python彩色螺旋线绘制引发的问题
Nov 23 Python
解决Python3.8用pip安装turtle-0.0.2出现错误问题
Feb 11 Python
15行Python代码实现免费发送手机短信推送消息功能
Feb 27 Python
Pytorch损失函数nn.NLLLoss2d()用法说明
Jul 07 Python
Python提取PDF指定内容并生成新文件
Jun 09 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连接Oracle数据库
2006/10/09 PHP
mysq GBKl乱码
2006/11/28 PHP
php5.2时间相差8小时
2007/01/15 PHP
php获取当前网址url并替换参数或网址的方法
2010/06/06 PHP
页面乱码问题的根源及其分析
2013/08/09 PHP
PHP中文乱码解决方案
2015/03/05 PHP
php实现断点续传大文件示例代码
2020/06/19 PHP
网站导致浏览器崩溃的原因总结(多款浏览器) 推荐
2010/04/15 Javascript
Js从头学起(基本数据类型和引用类型的参数传递详细分析)
2012/02/16 Javascript
jquery中的常见问题及快速解决方法小结
2016/06/14 Javascript
JavaScript职责链模式概述
2016/09/17 Javascript
基于JavaScript实现Tab选项卡切换效果
2016/11/24 Javascript
JS使用正则实现去掉字符串左右空格的方法
2016/12/27 Javascript
bootstrap动态添加面包屑(breadcrumb)及其响应事件的方法
2017/05/25 Javascript
Vue2.0用 watch 观察 prop 变化(不触发)
2017/09/08 Javascript
angular6.x中ngTemplateOutlet指令的使用示例
2018/08/09 Javascript
使用Vue CLI创建typescript项目的方法
2019/08/09 Javascript
javascrpt密码强度校验函数详解
2020/03/18 Javascript
javascript实现拼图游戏
2021/01/29 Javascript
[07:54]DOTA2-DPC中国联赛 正赛 iG vs VG 选手采访
2021/03/11 DOTA
将字典转换为DataFrame并进行频次统计的方法
2018/04/08 Python
python八皇后问题的解决方法
2018/09/27 Python
简单了解python中对象的取反运算符
2019/07/01 Python
Python3中的最大整数和最大浮点数实例
2019/07/09 Python
css3实现垂直下拉动画菜单示例
2014/04/22 HTML / CSS
HTML5拖拽文件到浏览器并实现文件上传下载功能代码
2013/06/06 HTML / CSS
Html5页面内使用JSON动画的实现
2019/01/29 HTML / CSS
ECCO爱步加拿大官网:北欧丹麦鞋履及皮具品牌
2017/07/08 全球购物
英国领先的维生素和补充剂品牌:Higher Nature
2019/08/26 全球购物
校庆活动策划方案
2014/06/05 职场文书
安全生产工作汇报材料
2014/10/28 职场文书
2014年酒店年度工作总结
2014/12/10 职场文书
学校运动会加油词
2015/07/18 职场文书
2015年六年级班主任工作总结
2015/10/15 职场文书
MySQL连接控制插件介绍
2021/09/25 MySQL
Golang实现可重入锁的示例代码
2022/05/25 Golang