通过源码分析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的id()函数解密过程
Dec 25 Python
python单线程实现多个定时器示例
Mar 30 Python
最大K个数问题的Python版解法总结
Jun 16 Python
python爬虫基本知识
Mar 05 Python
python使用openpyxl库修改excel表格数据方法
May 03 Python
python将字符串list写入excel和txt的实例
Jul 20 Python
利用keras加载训练好的.H5文件,并实现预测图片
Jan 24 Python
AUC计算方法与Python实现代码
Feb 28 Python
Django更新models数据库结构步骤
Apr 01 Python
Python爬虫之Selenium实现键盘事件
Dec 04 Python
5行Python代码实现一键批量扣图
Jun 29 Python
Python中itertools库的四个函数介绍
Apr 06 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中Cannot send session cache limiter 的问题的方法
2007/04/27 PHP
一个简洁实用的PHP缓存类完整实例
2014/07/26 PHP
PHP 生成N个不重复的随机数
2015/01/21 PHP
smarty模板判断数组为空的方法
2015/06/10 PHP
thinkPHP5.0框架URL访问方法详解
2017/03/18 PHP
php中的异常和错误浅析
2017/05/03 PHP
php实现支付宝当面付(扫码支付)功能
2018/05/30 PHP
php+Ajax处理xml与json格式数据的方法示例
2019/03/04 PHP
用javascript操作xml
2006/11/04 Javascript
自动生成文章摘要的代码[JavaScript 版本]
2007/03/20 Javascript
JavaScript 快捷键设置实现代码
2009/03/13 Javascript
Extjs学习笔记之五 一个小细节renderTo和applyTo的区别
2010/01/07 Javascript
javascript异步编程的4种方法
2014/02/19 Javascript
js和jquery如何获取图片真实的宽度和高度
2014/09/28 Javascript
JavaScript简介
2015/02/15 Javascript
jQuery实现向下滑出的平滑下拉菜单效果
2015/08/21 Javascript
jQuery Timelinr实现垂直水平时间轴插件(附源码下载)
2016/02/16 Javascript
用nodejs搭建websocket服务器
2017/01/23 NodeJs
js面向对象编程总结
2017/02/16 Javascript
vuex操作state对象的实例代码
2018/04/25 Javascript
express框架下使用session的方法
2019/07/31 Javascript
微信小程序中data-key属性之数据传输(经验总结)
2020/08/22 Javascript
[01:04:05]Mineski vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
Python中类型关系和继承关系实例详解
2015/05/25 Python
解决Pycharm后台indexing导致不能run的问题
2019/06/27 Python
python列表推导式入门学习解析
2019/12/02 Python
Django CSRF认证的几种解决方案
2020/03/03 Python
CSS3制作皮卡丘动画壁纸的示例
2020/11/02 HTML / CSS
Holland & Barrett爱尔兰:英国领先的健康零售商
2019/03/31 全球购物
写自荐信要注意什么
2013/12/26 职场文书
魅力教师事迹材料
2014/01/10 职场文书
大学生军训自我鉴定
2014/02/12 职场文书
委托书范文
2014/04/02 职场文书
安全教育演讲稿
2014/05/09 职场文书
财务检查整改报告
2014/11/06 职场文书
奥巴马开学演讲观后感
2015/06/12 职场文书