通过源码分析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中使用支持向量机(SVM)算法
Dec 26 Python
pycham查看程序执行的时间方法
Nov 29 Python
linux查找当前python解释器的位置方法
Feb 20 Python
Python+AutoIt实现界面工具开发过程详解
Aug 07 Python
Python 文件操作之读取文件(read),文件指针与写入文件(write),文件打开方式示例
Sep 29 Python
python3.8与pyinstaller冲突问题的快速解决方法
Jan 16 Python
python str字符串转uuid实例
Mar 03 Python
Python标准库shutil模块使用方法解析
Mar 10 Python
Python生成器常见问题及解决方案
Mar 21 Python
Python如何向SQLServer存储二进制图片
Jun 08 Python
python开发一款翻译工具
Oct 10 Python
anaconda升级sklearn版本的实现方法
Feb 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数据库代码
2009/03/10 PHP
匹配csdn用户数据库与官方用户的重合度并将重叠部分的用户筛选出来
2011/12/25 PHP
Sublime里直接运行PHP配置方法
2014/11/28 PHP
ThinkPHP3.1.x修改成功与失败跳转页面的方法
2017/09/29 PHP
javascript学习笔记(十八) 获得页面中的元素代码
2012/06/20 Javascript
9行javascript代码获取QQ群成员具体实现
2013/10/16 Javascript
基于jQuery仿淘宝产品图片放大镜代码分享
2020/06/23 Javascript
javascript针对cookie的基本操作实例详解
2015/11/30 Javascript
利用JS判断字符串是否含有数字与特殊字符的方法小结
2016/11/25 Javascript
JavaScript判断浏览器及其版本信息
2017/01/20 Javascript
vue2.0 实现导航守卫(路由守卫)
2018/05/21 Javascript
JS/HTML5游戏常用算法之路径搜索算法 A*寻路算法完整实例
2018/12/14 Javascript
Vue3.x源码调试的实现方法
2019/10/13 Javascript
Layui 解决表格异步调用后台分页的问题
2019/10/26 Javascript
JS在Array数组中按指定位置删除或添加元素对象方法示例
2019/11/19 Javascript
[01:09]2014DOTA2国际邀请赛 TI4西雅图DOTA2 中国美女coser加油助威
2014/07/20 DOTA
python中定义结构体的方法
2013/03/04 Python
深入理解python中的浅拷贝和深拷贝
2016/05/30 Python
matplotlib设置legend图例代码示例
2017/12/19 Python
python sys,os,time模块的使用(包括时间格式的各种转换)
2018/04/27 Python
使用Python AIML搭建聊天机器人的方法示例
2018/07/09 Python
如何安装并使用conda指令管理python环境
2019/07/10 Python
Python爬虫 批量爬取下载抖音视频代码实例
2019/08/16 Python
python编写softmax函数、交叉熵函数实例
2020/06/11 Python
Python pymysql模块安装并操作过程解析
2020/10/13 Python
python实现文件+参数发送request的实例代码
2021/01/05 Python
美国女性奢华品牌精品店:INTERMIX
2017/10/12 全球购物
Ruby如何实现动态方法调用
2012/11/18 面试题
鼓舞士气的口号
2014/06/16 职场文书
倡议书作文
2015/01/19 职场文书
公司感谢信范文
2015/01/22 职场文书
人事主管岗位职责
2015/02/04 职场文书
工作态度恶劣检讨书
2015/05/06 职场文书
实习指导老师意见
2015/06/04 职场文书
2019数学教师下学期工作总结
2019/06/27 职场文书
有关保护环境的宣传标语100条
2019/08/07 职场文书