通过源码分析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中的defaultdict模块和namedtuple模块的简单入门指南
Apr 01 Python
详解Python2.x中对Unicode编码的使用
Apr 03 Python
Python同时向控制台和文件输出日志logging的方法
May 26 Python
Python实现读取txt文件并画三维图简单代码示例
Dec 09 Python
Python机器学习之决策树算法
Dec 22 Python
VScode编写第一个Python程序HelloWorld步骤
Apr 06 Python
Python实现合并同一个文件夹下所有txt文件的方法示例
Apr 26 Python
Python3用tkinter和PIL实现看图工具
Jun 21 Python
使用pandas的box_plot去除异常值
Dec 10 Python
浅谈python累加求和+奇偶数求和_break_continue
Feb 25 Python
python在一个范围内取随机数的简单实例
Aug 16 Python
Python3压缩和解压缩实现代码
Mar 01 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
杏林同学录(二)
2006/10/09 PHP
php将会员数据导入到ucenter的代码
2010/07/18 PHP
PHP flock 文件锁详细介绍
2012/12/29 PHP
PHP+Ajax检测用户名或邮件注册时是否已经存在实例教程
2014/08/23 PHP
php中file_exists函数使用详解
2015/05/08 PHP
yii2 页面底部加载css和js的技巧
2016/04/21 PHP
PHP addAttribute()函数讲解
2019/02/03 PHP
一些常用的JS功能函数(2009-06-04更新)
2009/06/04 Javascript
解析jquery获取父窗口的元素
2013/06/26 Javascript
使用jquery实现以post打开新窗口
2014/03/19 Javascript
jQuery对val和atrr(&quot;value&quot;)赋值的区别介绍
2014/09/26 Javascript
推荐9款炫酷的基于jquery的页面特效
2014/12/07 Javascript
JavaScript计算某一天是星期几的方法
2015/08/05 Javascript
javascript实现支持移动设备画廊
2015/08/24 Javascript
jQuery鼠标事件汇总
2015/08/30 Javascript
JSON格式的时间/Date(2367828670431)/格式转为正常的年-月-日 格式的代码
2016/07/27 Javascript
ES6中参数的默认值语法介绍
2017/05/03 Javascript
JavaScript之DOM_动力节点Java学院整理
2017/07/03 Javascript
nodejs用gulp管理前端文件方法
2018/06/24 NodeJs
python 多线程对post请求服务器测试并发的方法
2019/06/13 Python
python开发之anaconda以及win7下安装gensim的方法
2019/07/05 Python
python模拟鼠标点击和键盘输入的操作
2019/08/04 Python
Python实现TCP通信的示例代码
2019/09/09 Python
Windows+Anaconda3+PyTorch+PyCharm的安装教程图文详解
2020/04/03 Python
CSS3教程(7):CSS3嵌入字体
2009/04/02 HTML / CSS
CSS3 Backgrounds属性相关介绍
2011/05/11 HTML / CSS
岗位职责的含义
2013/11/17 职场文书
教师自我剖析材料
2014/09/29 职场文书
晋江市人民政府党组群众路线教育实践活动整改方案
2014/10/25 职场文书
2014年环卫工作总结
2014/11/22 职场文书
2015婚礼主持词开场白
2015/05/28 职场文书
同学聚会祝酒词
2015/08/10 职场文书
《将心比心》教学反思
2016/02/23 职场文书
2019个人工作自我评价范文(3篇)
2019/09/19 职场文书
Django rest framework如何自定义用户表
2021/06/09 Python
python​格式化字符串
2022/04/20 Python