3.1.1 元组
元组这部分,无论是在小甲鱼课程还是在《Python编程》中都有讲过,这里就补充一下关于元组的其他知识吧。
交换变量名:
In [1]: a,b,c = 1,2,3,
In [2]: a
Out[2]: 1
In [3]: b
Out[3]: 2
In [4]: c
Out[4]: 3
In [5]: c,a,b = a,b,c
In [6]: a
Out[6]: 2
In [7]: b
Out[7]: 3
In [8]: c
Out[8]: 1
拆包常用场景:遍历元组或列表组成的序列
In [9]: seq = [(1,2,3,),(4,5,6),(7,8,9)]
In [10]: for a,b,c in seq:
...: print('a={0},b={1},c={2}'.format(a,b,c))
...:
a=1,b=2,c=3
a=4,b=5,c=6
a=7,b=8,c=9
关于format的描述还是比较复杂的,有需要的建议去看这篇文章,写的还是蛮详细的:https://blog.csdn.net/jpch89/article/details/84099277
拆包工具*rest,包含未定义的剩下的所有数值:
In [17]: vul = (1,2,3,4,5,6)
In [18]: a,b,*rest = vul
In [19]: a
Out[19]: 1
In [20]: b
Out[20]: 2
In [21]: rest
Out[21]: [3, 4, 5, 6]
3.1.2.2 链接和联合列表
如果你有一个已经定义的列表,你可以用extend方法向该列表添加多个元素:
In [25]: x=[4,None,666]
In [26]: x.extend([777,888,999])
In [27]: x
Out[27]: [4, None, 666, 777, 888, 999]
In [29]: y = ['aa','bb','cc']
In [30]: y
Out[30]: ['aa', 'bb', 'cc']
In [31]: x
Out[31]: [4, None, 666, 777, 888, 999]
In [32]: x.extend(y)
In [33]: x
Out[33]: [4, None, 666, 777, 888, 999, 'aa', 'bb', 'cc']
3.1.2.3 排序
根据字符串的长度进行paixu
In [91]: o = ['xinyu','quanxiaosheng','wang','da','chui']
In [92]: o.sort(key=len)
In [93]: o
Out[93]: ['da', 'wang', 'chui', 'xinyu', 'quanxiaosheng']
3.1.2.4 二分搜索和已排序列表的维护
内建的bisect模块实现了二分搜索和已排序列表的差值。bisect.bisect会找到元素应当被插入的位置,并保持序列排序,而bisect.insort将元素插入到响应位置:
In [42]: import bisect
In [43]: x = [1,2,3,4,5,6,7]
In [44]: bisect.bisect(x,2)
Out[44]: 2
In [45]: bisect.bisect(x,3)
Out[45]: 3
In [46]: bisect.insort(x,7)
In [47]: x
Out[47]: [1, 2, 3, 4, 5, 6, 7, 7]
3.1.3 内建序列函数
我们经常需要在遍历一个序列的同时追踪当前元素的索引。由于这种场景很常见,所以Python内建了enumerate函数,返回了(i,value)元组的序列,其中value是元素的值,i是元素的索引:
In [51]: x
Out[51]: [1, 2, 3, 4, 5, 6, 7, 7]
In [52]: lue= {}
In [53]: for i,v in enumerate(x):
...: lue[v]=i
...:
In [54]: lue
Out[54]: {1: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 7}
这个功能是蛮好用的。
3.1.3.2 sorted
sorted函数返回一个根据任意序列中的元素新建的已排序列表:
In [56]: x
Out[56]: [5, 7, 9, 2, 7, 5, 6, 2]
In [57]: sorted(x)
Out[57]: [2, 2, 5, 5, 6, 7, 7, 9]
In [59]: sorted('hello world')
Out[59]: [' ', 'd', 'e', 'h', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
3.1.3.3 zip
3.1.3.3 zip
zip将列表、元组或其他序列的元素配对,新建一个元组构成的序列:
In [60]: x = ['piaoxiaomin','piaozhiyan','quanxiaosheng']
In [61]: y = ['xinyuanjieyi','changzeyamei','kubeizhenxi']
In [62]: z = zip(x,y)
In [63]: list(z)
Out[63]:
[('piaoxiaomin', 'xinyuanjieyi'),
('piaozhiyan', 'changzeyamei'),
('quanxiaosheng', 'kubeizhenxi')]
zip可以处理任意长度的序列,它生成的列表长度由最短的序列决定:
In [65]: x1 = ['666','777']
In [68]: z = zip(x,y,x1)
In [69]: list(z)
Out[69]: [('piaoxiaomin', 'xinyuanjieyi', '666'), ('piaozhiyan', 'changzeyamei', '777')]
zip的常用场景为同事遍历多个序列,有时候会和enumerate同时使用:
In [71]: for i,(a,b) in enumerate (zip(x,y)):
...: print('{0}:{1},{2}'.format(i,a,b))
...:
0:piaoxiaomin,xinyuanjieyi
1:piaozhiyan,changzeyamei
2:quanxiaosheng,kubeizhenxi
zip函数还有一种拆分已配对序列的方式。这种方式的另一种思路就是将行的列表转换为列的列表:
In [81]: list(x)
Out[81]: ['piaoxiaomin', 'piaozhiyan', 'quanxiaosheng']
In [82]: y
Out[82]: ['xinyuanjieyi', 'changzeyamei', 'kubeizhenxi']
In [83]: z = zip(x,y)
In [84]: v = list(z)
In [85]: v
Out[85]:
[('piaoxiaomin', 'xinyuanjieyi'),
('piaozhiyan', 'changzeyamei'),
('quanxiaosheng', 'kubeizhenxi')]
In [86]: f,l = zip(*v)
In [87]: f
Out[87]: ('piaoxiaomin', 'piaozhiyan', 'quanxiaosheng')
In [88]: l
Out[88]: ('xinyuanjieyi', 'changzeyamei', 'kubeizhenxi')
3.1.3.4 reversed
reversed 函数将序列的元素倒序排列:
In [90]: list(reversed(range(10)))
Out[90]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
3.1.4 字典
删除:del
In [100]: p = {1:'a',2:'b',3:'c'}
In [101]: del p[1]
In [102]: p
Out[102]: {2: 'b', 3: 'c'}
删除并返回值:pop
In [107]: p = {1:'a',2:'b',3:'c'}
In [108]: p.pop(2)
Out[108]: 'b'
In [109]: p
Out[109]: {1: 'a', 3: 'c'}
kyes方法和values方法会分别为你提供字典键、值的迭代器。然而键值对并没有特定的顺序,这些函数输出的键、值都是按照相同的顺序,还可以通过updated 将两个字典合并 ,key值如果相同,就会覆盖:
In [120]: p = {1:'changzeyamei',2:'xinyuanjieyi'}
In [122]: p.update({3:'quanxiaosheng',4:'piaoxiaomin'})
In [123]: p
Out[123]: {1: 'changzeyamei', 2: 'xinyuanjieyi', 3: 'quanxiaosheng', 4: 'piaoxiaomin'}
3.1.4.1 从序列生成字典
通常情况下,你会有两个序列想要在字典中按元素配对。由于字典本质是2-元组(含有两个元组)的集合,字典时可以接受2-元组的列表作为参数的:
In [133]: map = dict(zip(range(5),reversed(range(5))))
In [134]: map
Out[134]: {0: 4, 1: 3, 2: 2, 3: 1, 4: 0}
或者
In [136]: x = ['xinyuan','changze','quan']
In [137]: y = ['jieyi','yamei','xiaosheng']
In [138]: name = dict(zip(x,y))
In [139]: name
Out[139]: {'xinyuan': 'jieyi', 'changze': 'yamei', 'quan': 'xiaosheng'}
3.1.4.2 默认值
假设我们有以下这样的代码逻辑:
In [200]: x
Out[200]: {'changze': 'yamei', 'xinyuan': 'jieyi', 'quan': 'xiaosheng'}
In [201]: if 'changze' in x :
...: va = x['changze']
...: else:
...: va = 'byebye'
...:
In [202]: va
Out[202]: 'yamei'
字典的get方法和pop方法可以返回一个默认值,因此上述的if-else代码块可以被简写为:
va = x.get('changze','byebye')
带有默认值的get方法会在key参数不是字典的键时返回None,而pop会抛出异常,一个常见的场景是字典中的值集合通过设置,成为另一种集合,比如列表。举个例子,将字词组成的列表根据首字母分类为包含列表的字典:
In [217]: zimu = ['ab','ac','ad','ba','bc','bd']
In [218]: kong = {}
In [219]: for i in zimu:
...: chui = i[0]
...: if chui not in kong:
...: kong[chui] =[i]
...: else:
...: kong[chui].append(i)
...:
In [220]: kong
Out[220]: {'a': ['ab', 'ac', 'ad'], 'b': ['ba', 'bc', 'bd']}
字典的setdefault方法就是为了这个目的而产生的。上述的for循环语句可以被写为:
In [223]: kong = {}
In [224]: for i in zimu:
...: chui = i[0]
...: kong.setdefault(chui,[]).append(i)
...:
In [225]: kong
Out[225]: {'a': ['ab', 'ac', 'ad'], 'b': ['ba', 'bc', 'bd']}
关于setdefault的补充:查询第一个值是否在字典内,如果不在,则返回一个值,这个值就是第二个自己定义的值。
In [230]: kong.setdefault('quanxiaosheng','quanxiaosheng')
Out[230]: 'quanxiaosheng'
In [231]: kong
Out[231]:
{'a': ['ab', 'ac', 'ad'],
'b': ['ba', 'bc', 'bd'],
'quanxiaosheng': 'quanxiaosheng'}
那么如果quanxiaosheng这个值存在呢?那么就不会返回默认值。
In [234]: kong.setdefault('quanxiaosheng','wobuzai')
Out[234]: 'quanxiaosheng'
In [235]: kong
Out[235]:
{'a': ['ab', 'ac', 'ad'],
'b': ['ba', 'bc', 'bd'],
'quanxiaosheng': 'quanxiaosheng'}
3.1.4.3 有效的字典键类型
尽管字典的值可以是任何Python对象,但键必须是不可变的对象,比如标量类型(整数、浮点数、字符串)或元组(且元组内对象也必须是不可变对象)。这里要使用到一个术语叫哈希化,通过hash函数可以检查一个对象是否可以哈希化(即是否可以用作字典的键):
In [236]: hash('string')
Out[236]: 2024487268729617358
In [237]: hash((1,2,3))
Out[237]: 529344067295497451
In [238]: hash(['wangdachui']) # 列表不可以成为字典的键,所以报错了
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-238-e9ad34c4cdd2> in <module>
----> 1 hash(['wangdachui'])
TypeError: unhashable type: 'list'
为了将列表作为键,一种方式就是将其转换为元组,而元组只要它内部元素都可以哈希化,则它自己也可哈希化:
In [243]: d = {}
In [244]: d[tuple([1,2,3])] = 5
In [245]: d
Out[245]: {(1, 2, 3): 5}
In [246]: hash(d)
3.1.5 集合
集合是一种无序且元素唯一的容器。你可以认为集合也像字典,但是只有键没有值。集合可以有两种创建方式:通过set函数或者是用字面值集与大括号的语法:
In [247]: set([2,3,4])
Out[247]: {2, 3, 4}
In [248]: {2,3,4}
Out[248]: {2, 3, 4}
集合支持数学上的集合操作:例如联合、交集、差集、对称差集。两个集合的联合就是两个集合中不同元素的并集。可以通过union方法或|二元操作符完成:
In [250]: a = {'a','b','c','d'}
In [251]: b = {1,2,3,4}
In [252]: a.union(b)
Out[252]: {1, 2, 3, 4, 'a', 'b', 'c', 'd'}
In [253]: a|b
Out[253]: {1, 2, 3, 4, 'a', 'b', 'c', 'd'}
交集包含了两个集合中同时包含的元素。可以使用&操作符或intersection方法获得交集:
In [254]: c ={3,4,5,6}
In [255]: b.intersection(c)
Out[255]: {3, 4}
In [256]: b & c
Out[256]: {3, 4}
以下是常用的集合方法列表:
所有的逻辑集合运算都有对应的操作,允许你用操作的结果代替操作左边的集合内容:
In [257]: a
Out[257]: {'a', 'b', 'c', 'd'}
In [258]: a |= b
In [259]: a
Out[259]: {1, 2, 3, 4, 'a', 'b', 'c', 'd'}
和字典类似,集合的元素必须是不可变的。如果想要包含列表型的元素,必须先转换为元组:
In [260]: data = [1,2,3,4]
In [261]: my = {tuple(data)}
In [262]: my
Out[262]: {(1, 2, 3, 4)}
还可以检查一个集合是否是另一个集合的子集(包含于)或超集(包含):
In [267]: {1,2,3}.issubset(my)
Out[267]: True
In [269]: my.issuperset({4,5,6})
Out[269]: True
当且仅当两个集合的内容一模一样时,两个集合才相等:
In [266]: my = {1,2,3,4,5,6}
In [270]: ym = {2,3,6,5,1,4}
In [271]: ym == my
Out[271]: True
3.1.6 列表、集合和字典的推导式
在python界里,如何优雅的喝咖啡,单手敲代码还依然游刃有余?很简单,学会推导式,一行解决人家五行的内容。
col = ['a','b','c','d','e','f']
kong = []
for val in col:
if col:
kong.append(val)
print(kong)
共七行代码,现在,单手拿起咖啡,开始用推导式:
col = ['a','b','c','d','e','f']
kong = [val for val in col if col]
print(kong)
三行搞定
其他场景:
给定一个字符串列表,我们可以过滤出长度大于2的,你并且将字母改为大写:
In [272]: col = ['a123','123b','434c','d253','6e','f']
In [273]: [val.upper() for val in col if len(col)>2]
Out[273]: ['A123', '123B', '434C', 'D253', '6E', 'F']
集合与字典的推导式是列表推导式的自然拓展,用相似的方式生成集合与字典。字典推导式如下所示:
In [275]: dictt= {'keys' :[val.upper() for val in col if len(col)>2]}
In [276]: dictt
Out[276]: {'keys': ['A123', '123B', '434C', 'D253', '6E', 'F']}
集合推导式看起来很像列表推导式,只是中括号变成了大括号:
In [277]: col = ['a123','123b','434c','d253','6e','f']
In [278]: dictt = {val.upper() for val in col if len(col)>2}
In [279]: dictt
Out[279]: {'123B', '434C', '6E', 'A123', 'D253', 'F'}
假设我们想要一个集合,集合里包含列表中字符串的长度,我可以通过集合推导式很方便地实现。注意:集合内不会出现重复的元素:
In [280]: col = ['a123','123b','434c','d253','6e','f']
In [281]: len = {len(val) for val in col}
In [282]: len
Out[282]: {1, 2, 4}
我们也可以使用map函数更函数化、更简洁地表达:
In [2]: col = ['a123','123b','434c','d253','6e','f']
In [3]: set(map(len,col))
Out[3]: {1, 2, 4}
我们创建一个将字符串预期位置相匹配的字典作为字典推导式的简单示例:
col = ['a123','123b','434c','d253','6e','f']
dictt = {val: index for index,val in enumerate(col)}
print(dictt)
3.1.6.1 嵌套列表推导式
从列表中筛选出同时包含两个'4'的元素:
In [4]: col = [['a123','123b','434c','d253','6e','f'],['454','777','449']]
In [5]: dictt =[name for names in col for name in names
...: if name.count('4')>=2 ]
In [6]: dictt
Out[6]: ['434c', '454', '449']
将包含三个元组的元组转化为一个整数列表:
In [7]: col = ((1,2,3),(4,5,6),(7,8,9))
In [8]: re = [name for names in col for name in names ]
In [9]: re
Out[9]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
还有最后一个列表推导式:
In [10]: col = ((1,2,3),(4,5,6),(7,8,9))
In [11]: re = [[name for name in names ] for names in col ]
In [12]: re
Out[12]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
第三章的内容很多部分都是对基础知识的拔高,难度还是比较高的。所以我打算分成上下两章去完成。
嗯……
胭惜雨
2021年03月19日