通过源码分析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 ORM框架SQLAlchemy学习笔记之安装和简单查询实例
Jun 10 Python
Python中__call__用法实例
Aug 29 Python
Python基于递归算法实现的汉诺塔与Fibonacci数列示例
Apr 18 Python
Python实现的朴素贝叶斯算法经典示例【测试可用】
Jun 13 Python
关于python写入文件自动换行的问题
Jun 23 Python
python3 拼接字符串的7种方法
Sep 12 Python
Python tkinter和exe打包的方法
Feb 05 Python
python 控制台单行刷新,多行刷新实例
Feb 19 Python
Tensorflow中批量读取数据的案列分析及TFRecord文件的打包与读取
Jun 30 Python
如何利用pycharm进行代码更新比较
Nov 04 Python
PyCharm 光标变成黑块的解决方式
Feb 06 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
收音机另类DIY - 纸巾盒做外壳
2021/03/02 无线电
php中simplexml_load_file函数用法实例
2014/11/12 PHP
php上传文件常见问题总结
2015/02/03 PHP
PHP删除指定目录中的所有目录及文件的方法
2015/02/26 PHP
分享一个漂亮的php验证码类
2016/09/29 PHP
php连接MSsql server的五种方法总结
2018/03/04 PHP
Laravel学习笔记之Artisan命令生成自定义模板的方法
2018/11/22 PHP
javascript new 需不需要继续使用
2009/07/02 Javascript
在模板页面的js使用办法
2010/04/01 Javascript
js换图片效果可进行定时操作
2014/06/09 Javascript
浅谈JavaScript中运算符的优先级
2015/07/07 Javascript
javascript中sort() 方法使用详解
2015/08/30 Javascript
归纳下js面向对象的几种常见写法总结
2016/08/24 Javascript
javascript中异常处理案例(推荐)
2016/10/03 Javascript
浅谈AngularJS中ng-class的使用方法
2016/11/11 Javascript
浅谈JS中的反柯里化( uncurrying)
2017/08/17 Javascript
简述vue状态管理模式之vuex
2018/08/29 Javascript
javascript自定义右键菜单插件
2019/12/16 Javascript
用Python的线程来解决生产者消费问题的示例
2015/04/02 Python
python中日志logging模块的性能及多进程详解
2017/07/18 Python
python+matplotlib实现动态绘制图片实例代码(交互式绘图)
2018/01/20 Python
使用css3绘制出各种几何图形
2016/08/17 HTML / CSS
纯CSS3实现鼠标滑过按钮动画第二节
2020/07/16 HTML / CSS
瑞典时尚耳机品牌:Urbanears
2017/07/26 全球购物
校班主任推荐信范文
2013/12/03 职场文书
原材料检验岗位职责
2014/03/15 职场文书
安全生产月演讲稿
2014/05/09 职场文书
2014审计局领导班子民主生活会对照检查材料思想汇报
2014/09/20 职场文书
工作经历证明书范文
2014/11/02 职场文书
幼儿园保育员随笔
2015/08/14 职场文书
《花钟》教学反思
2016/02/17 职场文书
2016先进集体事迹材料范文
2016/02/25 职场文书
导游词之泰山玉皇顶
2019/12/23 职场文书
解析目标检测之IoU
2021/06/26 Python
微信小程序scroll-view不能左右滑动问题的解决方法
2021/07/09 Javascript
Elasticsearch 基本查询和组合查询
2022/04/19 Python