数据结构和序列
本文简要介绍Python的常用数据结构:元组、列表、字典和集合。
元组
元组是一种固定长度、不可变的Python对象。
列表
列表是长度可变、内容可修改的Python对象。
内建序列函数
- enumerate函数
for i,value in enumerate(collection):
利用enumerate函数,可以在遍历一个序列的同时追踪当前元素的索引,value是元素的值,i是元素的索引。
- sorted函数
sorted函数返回一个根据任意序列中的元素新建的已排序列表:
In [21]: sorted([1,4,6,2,35,7])
Out[21]: [1, 2, 4, 6, 7, 35]
In [21]: sorted('horse race')
Out[22]: [' ', 'a', 'c', 'e', 'e', 'h', 'o', 'r', 'r', 's']
- zip
zip将列表、元组或其他序列的元素配对,新建一个元组构成的列表:
In [23]: seq1=['foo','bar','baz']
In [24]: seq2=['one','two','three']
In [25]: zipped=zip(seq1,seq2)
In [26]: list(zipped)
Out[26]: [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
zip可以处理任意长度的序列,它生成的列表长度由最短的序列决定:
In [27]: seq3=[False,True]
In [28]: list(zip(seq1,seq2,seq3))
Out[28]: [('foo', 'one', False), ('bar', 'two', True)]
zip的常用场景为同时遍历多个序列,与enumerate一起使用:
In [29]: for i,(a,b) in enumerate(zip(seq1,seq2)):
print('{0}: {1}, {2}'.format(i,a,b))
0: foo, one
1: bar, two
2: baz, three
给定一个已配对的序列(x)时,可以用a,b=zip(*x)来拆分序列。可以理解为将行的列表转换为列的列表。
In [31]: table_tennis_player=[('Ma','Long'),('Zhang','Jike'),('Xu','Xin')]
In [32]: xing,ming=zip(*table_tennis_player)
In [33]: xing
Out[33]: ('Ma', 'Zhang', 'Xu')
In [34]: ming
Out[34]: ('Long', 'Jike', 'Xin')
- reversed
reversed函数将序列的元素倒序排列:
In [36]: list1=[2,4,6,8,3]
In [37]: list(reversed(list1))
Out[38]: [3, 8, 6, 4, 2]
字典
又名哈希表 或者关联数组,字典是键值对集合,键和值都是Python对象。
从序列生成字典
将两个序列写入一个字典中,并两两配对:
In [60]: key_list=[1,2,3]
In [61]: value_list=[4,5,6]
In [62]: for key,value in zip(key_list,value_list):
mapping[key]=value
In [63]: mapping
Out[63]: {1: 4, 2: 5, 3: 6}
另一种实现方式:
In [64]: mapping=dict(zip(value_list,key_list))
In [65]: mapping
Out[65]: {4: 1, 5: 2, 6: 3}
有效的字典键类型
尽管字典的值可以是任何Python对象,但键必须是不可变对象,比如标量类型(整数、浮点数、字符串)或元组(且元组内对象也必须为不可变对象)。通过hash函数可以检查一个对象是否可以哈希化(即是否可以用做字典的键)。
In [66]: hash('string')
Out[66]: 7396311256443745595
In [67]: hash('5')
Out[67]: 7143329398867365923
In [68]: hash((2,5))
Out[68]: -8422800446983873125
In [69]: hash(2,[3,5])
Traceback (most recent call last):
File "C:\Users\lenovo\AppData\Local\Temp/ipykernel_3664/1260883256.py", line 1, in <module>
hash(2,[3,5])
TypeError: hash() takes exactly one argument (2 given)
集合
集合是一种无序且元素唯一的容器(可以理解成没有键,只有值的字典),利用set函数或者大括号来创建集合。
In [70]: set([2,2,2,1,3,3])
Out[70]: {1, 2, 3}
In [71]: {2,2,2,1,3,3}
Out[71]: {1, 2, 3}
两个集合的联合也就是两个集合求并集,通过union方法或 | 操作符完成。
In [72]: a={1,3,5,7,9}
In [73]: b={2,4,6,8,10}
In [74]: a.union(b)
Out[74]: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
In [75]: a | b
Out[75]: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
两个集合求交集,通过intersection方法或 & 操作符完成。
In [77]: c={1,2,3,4,5}
In [78]: d={1,3,5}
In [79]: c.intersection(d)
Out[79]: {1, 3, 5}
In [80]: c & d
Out[80]: {1, 3, 5}
表1:Python集合操作
函数 | 替代方法 | 描述 |
a.add(x) | N/A | 将元素x加入集合a |
a.clear() | N/A | 清空集合a |
a.remove(x) | N/A | 清除集合a中的元素x |
a.pop() | N/A | 移除集合a中的任意元素,如果集合a为空,抛出keyError |
a.union(b) | a | b | 求两集合的并集 |
a.update(b) | a |= b | a集合=两集合的并集 |
a.intersection(b) | a & b | 求两集合的交集 |
a.intersection_update(b) | a &= b | a集合=两集合的交集 |
a.difference(b) | a - b | a有b没有的元素 |
a.difference_update(b) | a -= b | a集合=a有b没有的元素 |
a.symmetric_difference(b) | a ^ b | 在a或b中,且不同时存在于a和b中的元素 |
a.symmetric_difference_update(b) | a ^= b | a=以上所叙述的元素 |
a.issubset(b) | N/A | a包含于b,返回True |
a.issuperset(b) | N/A | a包含b,返回True |
a.isdisjoint(b) | N/A | a与b无交集,返回True |
列表、集合和字典的推导式
列表推导式是一个将某个列表中符合条件的元素过滤出来,保存在一个新的列表中。
语法格式为:
[expr for val in collection if condition]
#与下面的for循环等价
result=[]
for val in collection:
if condition:
result.append(expr)
例如:给定一个列表字符串,过滤出长度大于2的,并将其字母改成大写:
In [90]: strings=['a','as','bat','car','dove','python']
In [91]: [x.upper() for x in strings if len(x)>2]
Out[91]: ['BAT', 'CAR', 'DOVE', 'PYTHON']
集合与字典推导式是列表推导式的自然拓展,
字典推导式语法格式:
dict_comp={key-expr:value-expr for value in collection if condition}
集合推导式语法格式(列表推导式的中括号换成了大括号):
set_comp={expr for value in collection if condition}
In [95]: loc_mapping={val:index for index,val in enumerate(strings)}
In [96]: loc_mapping
Out[96]: {'a': 0, 'as': 1, 'bat': 2, 'car': 3, 'dove': 4, 'python': 5}
嵌套列表推导式
#已有一个存放姓名的嵌套列表all_data,我想找到名字中包含两个及以上小写字母‘e’的对象,可以利用for循环遍历all_data中的所有元素,存放在一个临时列表names中,再遍历临时列表中的每一个值,找到符合条件的元素即可。
In [97]: all_data=[['John','Emily','Michael','Mary','Steven'],['Maria','Juan','Javier','Natalia','Pilar']]
In [98]: names_of_interest=[]
In [99]: for names in all_data:
...: enough_es=[name for name in names if name.count('e')>=2]
...: names_of_interest.append(enough_es)
...:
In [100]: names_of_interest
Out[100]: [['Steven'], []]
#也可以将上述操作放在一个嵌套列表推导式中完成,列表推导式的for循环部分是根据嵌套的顺序排列的,条件判断放在尾部。
In [101]: result = [name for names in all_data for name in names if name.count('e')>=2]
In [102]: result
Out[102]: ['Steven']
再来看一个例子:
In [103]: some_tuple=[(1,2,3),(4,5,6),(7,8,9)]
#已有一个列表,存放着3个元组,每个元组中有3个元素,将这9个元素存放在一个列表中,通过嵌套列表推导式来完成
In [104]: flattened=[x for tup in some_tuple for x in tup]
In [105]: flattened
Out[105]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
嵌套三层之后会影响代码的可读性,也容易出错,根据实际需要来使用。此外,嵌套推导式的语法要与列表推导式的语法区分开,列表推导式中可以继续嵌套列表推导式。
In [106]: [[x for x in tup] for tup in some_tuple]
Out[106]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
#上述代码利用列表推导式嵌套列表推导式创建了一个包含列表的列表。
Python for Data Analysis by Wes McKinney(O’Reilly).Copyright 2017 Wes McKinney,978-1-491-95766-0.