python中几个常见的“黑盒子”之 列表list
python常见的数据类型有:字符串,布尔类型,整数,浮点数,数字,日期,列表,元祖,字典。相信前面6个大家都非常的熟悉,但是对于python的列表,元祖,字典我有时候一直在想其内部的实现是怎么样子的,它们就像一个“黑盒子”一样,下面记录一下对于“列表 list”理解过程:
其实,在最开始我一直以为python的列表是通过链表实现的,直到一天,应该说是误打误撞,当我通过交互模式创建一个列表的时候,然后通过id()函数打印出列表中每个元素的地址时,我发现它们的地址是连续的,然后推测python的列表list不是我们传统意义上面的列表(虽然这个推测应该算是误打误撞吧,真实的原因不是这个,这个每个元素地址连续也是因为正好凑巧而已,但是却引起了我的进一步探索和思考),也就是说不是通过链表实现的(因为如果是链表的话其元素地址肯定是不连续的),然后我就猜测这种内存地址连续的情况,python的列表应该是通过数组的形式进行存储实现的。
通过查看python文档:https://wiki.python.org/moin/TimeComplexity,其中记录了这么一句话:Internally, a list is represented as an array;这也进一步验证了我推测的结果。然后查看python的源码:
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
其中记录了这么一个结构体(省略了源码中的大部分注释,有兴趣可以找到源码看一下),其中:
ob_item是指向列表对象的指针数组;
allocated是申请内存的槽的个数。
接着,通过源码获悉,python列表的存储形式就是以数组array的形式进行存储的,然后数组中每一个元素其实存储的是列表对象的指针。
现在我们搞明白了python列表的内部实现方式,所以进一步思考了如下问题:
1.对于列表操作,append操作肯定比insert操作效率要高:
因为,对于数组来说,用append操作从后面追加一个元素,时间复杂度来说是常量级的,但是用insert操作进行数组的插入来说,当插入第一位或者中间某一位的时候,该元素后面的所有元素都要相应的往后面挪动一位。
2.python中列表的存储形式是数组的形式,这也突出了其和链表的很大的区别:
a.当我们按照给定的索引值进行某一个元素的访问的话,数组的效率肯定是比链表的效率高出不少的。
因为,对于数组来说,我们可以直接计算出目标元素在内存中的具体位置,然后直接对其访问;但是对于链表来说,我们要做的是去遍历整个链表才可以得到目标元素。
b.但是对于插入insert操作来说,情况就和上面不同了。
因为对于链表来说,我们只要知道在哪里执行insert插入操作就可以了,无论该列表中含有多少元素,操作时间都基本是相同的,造作的成本非常低;但是对于数组就不同了,每次执行插入操作的时候,都需要移动插入点右边的所有元素。甚至有时候,因为插入的元素过多,开始分配的内存空间不够,还需要把这个列表元素整体搬到一个更大的数组中(这是我开始的猜测)。当然,对于这种情况,python的开发者早就想好了对应的方法,就是append操作通常会采用一种动态数组或向量的特定解决方案:
将内存分配得过大一些,并且等到其要溢出时,在线性时间内再次重新分配内存。
但是,这样似乎还是我上面猜测的那样,不是会让append和insert一样糟糕么,其实不是这样子的,因为就算这俩种情况都有可能去搬动大量的元素,但最主要的不同是:对于append操作,发生的可能性要小非常多,事实上,我们能够确保每次搬入的数组都大于原数组一定的比例,那么该操作的平均成本(每次搬动的开销平均分摊到每次append操作中去),这样的时间复杂度通常是常量级别的。这里也从而得出来,python的列表对内存的开销是比较大的。
python中几个常见的“黑盒子”之 列表list的更多相关文章
- python中几个常见的黑盒子之“字典dict” 与 “集合set”
这里说到"字典dict" 和 "集合set"类型,首先,先了解一下,对于python来说,标准散列机制是有hash函数提供的,对于调用一个__hash__方法: ...
- Python中执行系统命令常见的几种方法--转载
Python中执行系统命令常见的几种方法 Python中执行系统命令常见的几种方法有: (1)os.system # 仅仅在一个子终端运行系统命令,而不能获取命令执行后的返回信息 # 如果再命令行下执 ...
- python中集合set,字典dict和列表list的区别以及用法
python中set代表集合,list代表列表,dict代表字典 set和dict的区别在于,dict是存储key-value,每一个key都是唯一的,set相对于dict存储的是key,且key是唯 ...
- Python中3种内建数据结构:列表、元组和字典
Python中3种内建数据结构:列表.元组和字典 Python中有3种内建的数据结构:列表.元组和字典.参考简明Python教程 1. 列表 list是处理一组有序项目的数据结构,即你可以在一个列表中 ...
- (四)Python中的“四大才子”(字符串、列表、字典、集合)
前戏:在python中把数据序列分为可变(mutable)和不可变(immutable)两种 不可变:string.int.float.tuple 特点:相同对象只是占用一个内存地址,不管有多少个变量 ...
- python中字符串的常见操作方法
1. 字符串概念,字符串是一个容器,包含若干个字符并按照一定的顺序组织成一个整体.字符串支持索引操作. 2. 创建字符串基本语法 变量名 = "字符串信息" 变量名 = '字符串信 ...
- python中字符串的常见操作
demo:mystr = 'hello python' 1.find:mystr.find(str, start=0, end=len(mystr)),检测字符串中是否有要查询的字符,如果有返回开始的 ...
- Python中的数据类型常见方法
list() 方法 1.cmp(list1, list2):#比较两个列表的元素 2.len(list):#列表元素个数 3.max(list):#返回列表元素最大值 4.min(list):#返回列 ...
- python中几个常见的魔法方法
首先,什么是魔法方法呢?在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做"魔法"方法. __ init__()方法 当一个实例被创建的时候调用的初始 ...
随机推荐
- AspNetPager分页控件
AspNetPager分页控件解决了分页中的很多问题,直接采用该控件进行分页处理,会将繁琐的分页工作变得简单化,下面是我如何使用AspNetPager控件进行分页处理的详细代码:1.首先到www.we ...
- java后台如何获取String 类型 json里的字段值
首先把获取到的数据转为json String sbody=Json.getGson().toJson(resp.getResponseBody()); Huanxin 这个类是 json数据对应字段的 ...
- 时间服务器:NTP 服务器
15.1 关于时区与网络校时的通讯协议 使得每一部主机的时间同步化. DHCP 客户端/服务器端所需要的租约时间限制. 网络侦测时所需要注意的时间点.刚刚谈到的登录文件分析功能.具有相关性的主 ...
- JS实现关闭当前子窗口,刷新父窗口
一.JS实现关闭当前子窗口,刷新父窗口 JS代码如下: <script> function refreshParent() { window.opener.location.href = ...
- 前端mac下的工具
1.制作base64图片的 DataURLMaker imageAlpha 减少png图片尺寸 2.共享iphone X-Mirage 3.制作交互图 briefs 4.制作矢量图 sketch
- 转:python list排序的两种方法及实例讲解
对List进行排序,Python提供了两个方法 方法1.用List的内建函数list.sort进行排序 list.sort(func=None, key=None, reverse=False) Py ...
- php中Content-type说明
'hqx' -> 'application/mac-binhex40','cpt' -> 'application/mac-compactpro','doc' -> 'applica ...
- JS function
JS函数的定义 函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块. JS函数的声明 function functionname(){语句} 注意:function必须是小写,否则会报错. J ...
- Word2013创建目录
1.写好文档内容后,将光标移到标题行,点击“开始”里的“样式”->“创建样式”,为该标题创建一个新的样式,同时点击“修改”,在打开的窗口中选择左下方的“格式”,进行标题格式的调整.依次可设定子标 ...
- I2C总线和S5PV210的I2C总线控制器
一.什么是I2C通信协议? 1.物理接口:SCL + SDA (1)SCL(serial clock):时钟线,传输CLK信号,一般是I2C主设备向从设备提供时钟的通道. (2)SDA(serial ...