通过源码分析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通过pil模块将raw图片转换成png图片的方法
Mar 16 Python
Linux 下 Python 实现按任意键退出的实现方法
Sep 25 Python
Django admin美化插件suit使用示例
Dec 12 Python
python使用jieba实现中文分词去停用词方法示例
Mar 11 Python
Pycharm取消py脚本中SQL识别的方法
Nov 29 Python
python批量从es取数据的方法(文档数超过10000)
Dec 27 Python
Python实现Event回调机制的方法
Feb 13 Python
python实现控制COM口的示例
Jul 03 Python
django实现web接口 python3模拟Post请求方式
Nov 19 Python
python如何把字符串类型list转换成list
Feb 18 Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
Apr 26 Python
使用tensorflow实现VGG网络,训练mnist数据集方式
May 26 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中用正则表达式清除字符串的空白
2011/01/17 PHP
php学习之数据类型之间的转换介绍
2011/06/09 PHP
PHP加密函数 Javascript/Js 解密函数
2013/09/23 PHP
thinkphp模板输出技巧汇总
2014/11/24 PHP
php性能分析之php-fpm慢执行日志slow log用法浅析
2016/10/17 PHP
php实现mysql连接池效果实现代码
2018/01/25 PHP
PHP使用gearman进行异步的邮件或短信发送操作详解
2020/02/27 PHP
遨游,飞飞,IE,空中网 浏览器无提示关闭方法
2011/07/11 Javascript
JavaScript面向对象(极简主义法minimalist approach)
2012/07/17 Javascript
jquery三个关闭弹出层的小示例
2013/11/05 Javascript
jQuery实现点击后标记当前菜单位置(背景高亮菜单)效果
2015/08/22 Javascript
关于Bootstrap弹出框无法调用问题的解决办法
2016/03/10 Javascript
巧用jQuery选择器提高写表单效率的方法
2016/08/19 Javascript
js实现旋转木马效果
2017/03/17 Javascript
详解vue-cli + webpack 多页面实例应用
2017/04/25 Javascript
AngularJS实现的获取焦点及失去焦点时的表单验证功能示例
2017/10/25 Javascript
Vue cli3 库模式搭建组件库并发布到 npm的流程
2018/10/12 Javascript
详解在微信小程序的JS脚本中使用Promise来优化函数处理
2019/03/06 Javascript
在Python中用get()方法获取字典键值的教程
2015/05/21 Python
解决python xlrd无法读取excel文件的问题
2018/12/25 Python
Win10下Python3.7.3安装教程图解
2019/07/08 Python
Numpy的简单用法小结
2019/08/28 Python
Python urlopen()和urlretrieve()用法解析
2020/01/07 Python
django中嵌套的try-except实例
2020/05/21 Python
多视角3D可旋转的HTML5 Logo动画
2016/03/02 HTML / CSS
科颜氏香港官方网店:Kiehl’s香港
2021/03/07 全球购物
汉语言文学毕业生求职信
2013/10/01 职场文书
运动会领导邀请函
2014/02/05 职场文书
大专毕业生求职信
2014/07/05 职场文书
2014年质检员工作总结
2014/11/18 职场文书
本溪关门山导游词
2015/02/09 职场文书
暂停营业通知
2015/04/25 职场文书
投诉信回复范文
2015/07/03 职场文书
Python与C++中梯度方向直方图的实现
2022/03/17 Python
解决 redis 无法远程连接
2022/05/15 Redis
win10频率超出范围怎么办?win10老显示超出工作频率范围的解决方法
2022/07/07 数码科技