通过源码分析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网络编程学习笔记(四):域名系统
Jun 09 Python
详解Python中break语句的用法
May 14 Python
python 获取毫秒数,计算调用时长的方法
Feb 20 Python
详解Python 爬取13个旅游城市,告诉你五一大家最爱去哪玩?
May 07 Python
python找出一个列表中相同元素的多个索引实例
Jun 11 Python
Python PyQt5 Pycharm 环境搭建及配置详解(图文教程)
Jul 16 Python
python 实现人和电脑猜拳的示例代码
Mar 02 Python
python matplotlib.pyplot.plot()参数用法
Apr 14 Python
手把手教你如何用Pycharm2020.1.1配置远程连接的详细步骤
Aug 07 Python
Python3+Flask安装使用教程详解
Feb 16 Python
python实现web邮箱扫描的示例(附源码)
Mar 30 Python
Pytorch中使用ImageFolder读取数据集时忽略特定文件
Mar 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
队列在编程中的实际应用(php)
2010/09/04 PHP
php获取当前url地址的方法小结
2017/01/10 PHP
php查询及多条件查询
2017/02/26 PHP
javascript fullscreen全屏实现代码
2009/04/09 Javascript
JavaScript 直接操作本地文件的实现代码
2009/12/01 Javascript
jcarousellite.js 基于Jquery的图片无缝滚动插件
2010/12/30 Javascript
ExtJS下书写动态生成的xml(兼容火狐)
2013/04/02 Javascript
js获取上传文件大小示例代码
2014/04/10 Javascript
jQuery实现回车键(Enter)切换文本框焦点的代码实例
2014/05/05 Javascript
JS完整获取IE浏览器信息包括类型、版本、语言等等
2014/05/22 Javascript
js单独获取一个checkbox看其是否被选中
2014/09/22 Javascript
JavaScript实现select添加option
2015/07/03 Javascript
浅谈如何优雅处理JavaScript异步错误
2019/11/12 Javascript
使用BeautifulSoup爬虫程序获取百度搜索结果的标题和url示例
2014/01/19 Python
朴素贝叶斯算法的python实现方法
2014/11/18 Python
Python深入06——python的内存管理详解
2016/12/07 Python
使用Python &amp; Flask 实现RESTful Web API的实例
2017/09/19 Python
Python 爬取携程所有机票的实例代码
2018/06/11 Python
python 合并多个excel中同名的sheet
2021/01/22 Python
Canvas实现保存图片到本地的示例代码
2018/06/28 HTML / CSS
canvas实现扭蛋机动画效果的示例代码
2018/10/17 HTML / CSS
使用html5实现表格实现标题合并的实例代码
2019/05/13 HTML / CSS
国家地理在线商店:Shop National Geographic
2018/06/30 全球购物
幼儿园大班毕业感言
2014/02/06 职场文书
歌唱比赛主持词
2014/03/18 职场文书
实习公司领导推荐函
2014/05/21 职场文书
个人优缺点总结
2015/02/28 职场文书
4S店客服专员岗位职责
2015/04/07 职场文书
Nginx下配置Https证书详细过程
2021/04/01 Servers
MySQL COUNT函数的使用与优化
2021/05/10 MySQL
铁头也玩根德 YachtBoy YB-230......
2022/04/05 无线电
sql server偶发出现死锁的解决方法
2022/04/10 SQL Server
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
2022/05/04 Python
详解Mysql数据库平滑扩容解决高并发和大数据量问题
2022/05/25 MySQL
如何用H5实现好玩的2048小游戏
2022/07/23 HTML / CSS
zabbix如何添加监控主机和自定义监控项
2022/08/14 Servers