通过源码分析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 相关文章推荐
Python3.0与2.X版本的区别实例分析
Aug 25 Python
Python中创建字典的几种方法总结(推荐)
Apr 27 Python
Python快速排序算法实例分析
Nov 29 Python
Odoo中如何生成唯一不重复的序列号详解
Feb 10 Python
Python 从一个文件中调用另一个文件的类方法
Jan 10 Python
python对文件目录的操作方法实例总结
Jun 24 Python
Django model update的多种用法介绍
Mar 28 Python
django 信号调度机制详解
Jul 19 Python
使用python获取邮箱邮件的设置方法
Sep 20 Python
Python中操作各种多媒体,视频、音频到图片的代码详解
Jun 04 Python
Python+OpenCV图像处理——图像二值化的实现
Oct 24 Python
celery在python爬虫中定时操作实例讲解
Nov 27 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
Joomla下利用configuration.php存储简单数据
2010/05/19 PHP
解析PHP自带的进位制之间的转换函数
2013/06/08 PHP
Zend Framework教程之连接数据库并执行增删查的方法(附demo源码下载)
2016/03/21 PHP
PHP使用PDO操作sqlite数据库应用案例
2019/03/07 PHP
表单(FORM)的一些实用效果代码
2007/03/25 Javascript
ExtJs Excel导出并下载IIS服务器端遇到的问题
2011/09/16 Javascript
javascript中input中readonly和disabled区别介绍
2012/10/23 Javascript
javascript向后台传送相同属性的参数即数组参数
2014/02/17 Javascript
js面向对象之静态方法和静态属性实例分析
2015/01/10 Javascript
js实现兼容IE和FF的上下层的移动
2015/05/04 Javascript
IE8下jQuery改变png图片透明度时出现的黑边
2015/08/30 Javascript
jQuery实现为控件添加水印文字效果(附源码)
2015/12/02 Javascript
vue展示dicom文件医疗系统的实现代码
2018/08/27 Javascript
vue中img src 动态加载本地json的图片路径写法
2019/04/25 Javascript
Vue CLI2升级至Vue CLI3的方法步骤
2019/05/20 Javascript
Vue 如何使用props、emit实现自定义双向绑定的实现
2020/06/05 Javascript
Ant Design moment对象和字符串之间的相互转化教程
2020/10/27 Javascript
[01:27:30]LGD vs Newbee 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
Python设计模式之抽象工厂模式
2016/08/25 Python
Django实现组合搜索的方法示例
2018/01/23 Python
python3实现表白神器
2019/04/09 Python
html5将图片转换成base64的实例代码
2016/09/21 HTML / CSS
SteelSeries赛睿官网:游戏外设和配件的领先制造商(耳机、键盘、鼠标和鼠标垫)
2018/06/17 全球购物
市场营销专科应届生求职信
2013/11/24 职场文书
房地产出纳岗位职责
2013/12/01 职场文书
期末考试动员演讲稿
2014/01/10 职场文书
同事打架检讨书
2014/02/04 职场文书
收款委托书范本
2014/09/11 职场文书
学生顶撞老师的检讨书
2014/09/17 职场文书
学校政风行风整改方案
2014/10/25 职场文书
2014年文秘工作总结
2014/11/25 职场文书
师德标兵先进事迹材料
2014/12/19 职场文书
导游词范文
2015/02/13 职场文书
学校食堂食品安全承诺书
2015/04/29 职场文书
家长对学校的意见和建议
2015/06/03 职场文书
纯html+css实现奥运五环的示例代码
2021/08/02 HTML / CSS