您的位置:首页 > 理论基础 > 数据结构算法

python详解(2)--常用的数据结构(列表、元组、字典、集合)

2019-07-18 23:24 363 查看

最简单的python程序:

print('Hello World!')

即可将指定的字符串输出。
接下来首先了解python的常用数据类型(python中内置了很多已经实现的高级数据类型,但大都位于部分模块中,其会在之后python模块中详细叙述)。

一、列表(list)

python中的列表部分类似于其他编程语言的数组,其是可变对象,长度不固定,列表内的元素类型可不相同,列表内元素有顺序(显示顺序也即其在内存中的存储顺序),可以有空列表存在,使用

[]
进行标识,两个元素之间使用逗号分隔。

列表的创建:直接进行赋值如

listname = [1, 2, 3]
,或使用
list(变量)
创建列表,列表的创建是个迭代的过程,因此不可迭代的对象无法使用list()创建列表,字符串类型会产生
[‘h’, ’e’, ’l’, ’l’, ’o’]
此类列表。

列表的索引与切片:中括号记法,listname[x],其中x是元素下标(如图所示),可以直接使用listname[0] = 1此种方法对某个位置的元素进行赋值,

listname[a:b:c]
,其中a为开始位置(包括该位置),b为结束位置(不包括该位置),c为步长(默认为1),[:]表示制作列表的副本。
:①使用切片会生成副本,即其指向新的序列,对切片改动也不会改动原序列,如
b = a[:3]
,则创建副本;
②中括号记法允许尾端超出原范围,如
a = [1,2,3,4],b = a[2:6]
则b为[3,4],当尾端超出范围时默认缩短至原范围,注意直接使用a[index]下标索引时不允许超出范围,会报错;
③如果
a[:3]=xxx
直接赋值,则会修改原列表,此处的赋值意为将原列表中指定的部分替换为赋值的内容(必须是一个可迭代对象),两者的长度无需匹配,在原列表的基础上进行修改。

列表的遍历:使用for循环,另外有

for a,b in enumerate()
,可用来同时索引序号和内容,其中a为序号(下标),a,b变量不需要定义。

添加元素

listname.append(x)
,其中x为要添加到列表末尾的元素,改变了列表本身,此种方法效率高(具体原因在之后数据结构与算法部分详解);
listname.insert(a,b)
,其中a为要插入的位置的索引号(会插入到列表当前此索引元素之前),b为要插入的元素;
listname.extend(list_a)
,其中list为要整体放入原列表之后的列表,原列表变动。

修改元素:直接使用

listname[x] = 'a'
,x为索引号,可以将对应的元素直接更改。

删除元素

del listname[x]
来根据索引删除元素;
listname.remove('x')
其中x为元素值,可根据元素值删除,若元素不存在则报错,按列表顺序删除第一个匹配的元素;
listname.pop([i])
从列表的指定位置删除元素并将其返回,其中i可以省略默认为-1,即默认删除表末尾的元素;
listname.clear()
删除列表中的所有元素,相当于
del listname[:]
(上述两种删除方法原列表依然存在,只是删除了列表的元素,若使用
del listname
,则变量被删除,再使用需重新定义)。

列表的计算和排序

listname.count()
可返回指定元素出现的次数;
listname.index()
可返回指定元素在列表中第一次出现的位置(即索引号);
sum(listname,b)
可返回列表的统计结果加上b(b必须为数值);
len(listname)
可以返回列表的长度,即列表内共有多少个元素;
max/min(listname)
分别可返回列表内的最大元素和最小元素,对字符串等可迭代对象也支持,对字符按照字典序取最大元素;
listname.sort(key=None,reverse=False)
,在原列表的基础上进行排序,其中key表示从每个元素中提取一个用于比较的键,将其作为传入的函数对象的参数,对返回值进行排序,reverse只有两个值,True时为降序,False时为升序(默认为False),这个排序对中文不支持;
sorted(listname,key,reverse)
,sorted方法是建立一个原列表的副本并排序,不改变原列表的元素顺序;
listname.reverse()
就地倒排原列表;
listname.copy()
返回一个列表的副本(同listname[:])。

列表推导式:①

newlist = [表达式 for x in listname if …]
生成一个新列表,其中listname可以是生成器对象,if后的条件可以省略,表达式可以是x,int(x),(x,y)等(需有两个for迭代),例如若listname为range(a),则代表生成a个元素;
②列表推导式可进行循环嵌套,可以使写出的语句更简单,注意在表达式内不能进行赋值;
③列表推导式会将所有数据生成完毕后再执行下面的代码,而生成器则不然;
④列表推导式常与map/filter函数(后详)联用。

二维列表:即列表的元素也是列表,一般采用循环嵌套的方式进行创建,因此可使用

listname[][]
访问,没有其他特殊的语法。

二、元组(tuple)

元组是一个不可变的有顺序的序列,使用

”()”
进行标识,两个相邻元素间使用逗号分隔,元素的类型可以不同,其属性与列表类似,但注意元组不可变,因此列表的所有在原列表基础上的操作在元组中都不可执行。

元组的生成:生成元组时,若元组内只有一个元素,则表示为

('a',)
其中末尾的逗号不能省略,否则会视为非元组,空元组即用
()
表示,可以使用
a = tuple()
生成空元组。

元组推导式: newtuple = (表达式 for x in listname if …)与列表推导式的写法类似,但是其生成的不是一个元组,是一个生成器对象,需要转换或迭代,它不会将所有的数据都生成完毕后再执行下面的语句。

其他与列表的不同:①元组是不可变对象,列表中针对元素的操作元组中不适用,只能使用重新赋值的方法改变元组,需要注意,元组中的元素允许是可变对象,即元组本身和元组元素的内存地址都没有变化,但元组元素是可以变化的,如元组中的列表是可变的;
②元组可以作为字典的键,列表不可以;
③元组比列表的访问和处理速度快。

三、字典(dict)

是一个可变的无序对象,其中的元素均为键值对,通过键来索引并且键必须为不可变对象(键不能为列表,非不能通过列表创建),且键是唯一的(不允许一个字典中出现两个相同的键),使用

”{}”
进行标识,通过键来索引值的方法称为Hash算法。

字典的创建:①

dic = {‘a’:’b’,’c’:’d’}
,直接将键值对输入创建,可为空;
dict(zip(a,b))
创建,其中a,b为列表或元组,生成的是zip对象,zip函数可以将两个列表或元组组成一一对应的元组序列,变量个数需对应;
③使用给定的键值对创建字典,
dic = dict(a=b,c=d)
其中abcd均为变量,若直接键入未定义变量,则识别为变量名的字符串(此种方法虽然可行但并不建议);
dic = dict.fromkeys(listname)
,创建值为空,以列表中的元素为键的字典,注意此处的dict并非变量名。

删除

del dic
可删除字典及其变量名;
dic.clear()
可删除字典的全部元素,原字典变为空字典;
dic.pop(a)
会删除字典中以a为键的键值对并返回其值;
dic.popitem()
随机删除字典中的键值对并返回一个由被删除的键值对组成的元组。

访问:①通过键值对来访问字典,

dic.get(a,’b’)
其中a为指定的键,’b’为若没有查找到此键值对返回的值;
dic[key]
可返回对应的值(此种方式若指定的键不存在则会报错);
'key' in dic
是一个逻辑判断语句,判断键是否在字典中存在,返回布尔值。

遍历:①

for x in dic.items()
,其中x为字典中的元素,遍历出来的为由键值对组成的一个个元组;
②将上述items改为keys、values可遍历其键、值,注意遍历出来的为键值的原形式(直接使用
dic.keys()
会返回一个保存了字典所有键的迭代器);
③直接迭代字典
for i in dic
,则默认为对键的遍历,其相对于items遍历的效率更高,直接通过索引查找,items可能进行了相关转换。

添加、修改和删除元素:①

dic[key] = value
可直接向字典中添加元素,当key已存在时,替换原有value为新输入值;
del dic[key]
可直接删除字典中元素(删除了键值对),没有返回值;
dic.update(dic2)/.update(xx=1)
,意为将dic中key与dic2中相同的替换为dic2的value/若dic中有键’xx’则替换值为1,若无则添加键值对。

字典的初始化

dic.setdefault(k,0)
,其中k为键,0为初始化的值,其释义为如果k在dic中不存在则将此键添加并将值初始化为0,若存在则返回键k对应的值。
:在字典中,除非键值对存在,否则无法检查这个键的值,会产生KeyError,但可以检查键是否存在后再对其进行访问,一般常用上述
.get
方法,也可以使用in判断。

字典推导式:与列表推导式类似,如

dic = {i:random.randint(10,100) for i in range(7)}

总结:当需要随机的访问一个数据结构时,字典/嵌套字典是最优选择。注意字典中存储了key,但是value没有在字典中存储,而是指向某个内存对象。
dict.get(‘key’)相比较dict[‘key’]保证了程序的健壮性,因为若不确定key是否存在,则减少报错可能。

四、集合(set)

是一个可变无序不重复对象(存在不可变集合frozenset,

a = frozenset(x)
),使用“{}”或大写的拉丁字母进行标识,集合中的元素必须是不可变对象,因为集合会对每个元素作hash,即相当于以集合中的元素为键做字典。

创建

setname = {1,2,3}
直接输入元素,如果输入了重复元素会自动只保留一个;
setname = set(a)
,对a进行迭代,若a是字符串,则会将其拆分;
{}
表示一个空字典,因此创建空集合时采用
a = set()
实现。

元素的添加与删除

setname.add(a)
,注意a只能是不可变对象;
setname.update(a)
其中a可以是集合或者元素;
setname.remove(a)
删除指定元素,无返回值;
setname.pop()
删除随机元素并返回(因为集合是无序的);
setname.clear()
清空集合,
空集合打印出来显示为
“set()”

del setname
将集合与其指定的变量名全部删除。

集合关系判断

a.isdisjoint(b)
判断两个集合是否有共同元素,若无则返回True,若有则返回False;
a.issubset(b)
判断a是否为b的子集,若是则True,否则False;
a.issuperset(b)
判断a是否为b的母集,若是则True,否则False。

集合的交集、并集和差集运算

& 交集
| 并集
- 差集
^ 对称差集


如图所示,其中

交集:②;
并集:①+②+③;
差集:①或③;
对称差集:①+③。

集合推导式:与上述推导式类似。
:①推导式常用于“从一个空的新数据结构开始,循环处理一个已有的数据结构,并依据现有的数据生成新的数据,将其保存在新的数据结构中”;
②字典和集合对查找进行了专门的优化,因此若要进行查找相关的工作,使用字典和集合更优先;
③pprint模块内置了美观打印方法,可以使多嵌套的数据更易读,使用时需要先导入pprint模块,

pprint.pprint(a)


五、python中的深拷贝与浅拷贝与赋值

原理:python中的赋值,是将变量名绑定在对象上,即指向,因此在对对象进行操作时,若为可变对象,则所有绑在其上的变量都会同时变化;若为不可变对象,则实质上是创建了新的对象并将变量名绑在其上(可变对象变化时其内存ID不变)。

浅拷贝:①当被拷贝对象为不可变对象时,仅仅指向这个对象;
②当被拷贝对象时可变对象时,复制一个对象(内存ID变化)但其子对象的引用内存没有变化,因此若操作子对象,则拷贝后的对象也会变化(即内存ID改变,但子对象内存ID不变)(其可有两种书写方式,不使用copy模块的情况,

c=a.copy()
,在copy模块中
c=copy.copy(a)
);
③列表的切片也属于浅拷贝,使用变量传递的函数参数,传递的是引用,属于赋值操作。

深拷贝:①当被拷贝对象全部都为不可变对象时,仅仅指向这个对象;
②当被拷贝对象中拥有可变对象时,从上至下完全新建,即递归拷贝(内存ID改变)(在copy模块中,

d=copy.deepcopy(a)
),注意在递归拷贝中,对于基础的对象来说,可变对象新建,不可变对象依然是指向,即python中的基础不可变对象(如数字,字符串等)的内存地址永远不变,在已存在的情况下也不会新建。

:一定要注意如果a,b都是列表,a.append(b),则是在a中添加了对b的引用,如果不进行任何处理,则b变动时a中的b也会变动,即无法对一个可变对象循环添加且使其值不同,只能对其进行转换或拷贝。

其相对关系如表所示,意为对变量进行右述操作后其内存地址的变化情况。


小结:本文介绍了python中常用的几个数据容器/数据结构类型及其简单用法,对它们的原理并未作解释,后详。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: