老男孩教育-张导博客:https://www.cnblogs.com/Neeo/articles/11204572.html
1.列表
资料连接:https://www.cnblogs.com/Neeo/articles/11212368.html
列表是Python中最常用的数据类型之一,也是最灵活的数据类型之一,其可以包含任何种类的对象:数字、字符串、元组、字典,也可以嵌套的包含列表。当然与字符串不同的是,列表是可变的,可变指的是我们在原处修改其中的内容,如删除或增加一个元素,则列表中的其他元素自动缩短或者增长,也正是如此,在列表元素个数过多时,如果你删除靠前的(如第一个)元素,其他的元素都要向前移动,会导致性能有所下降,这是在开发中需要注意的。
1.1 创建列表
在Python中,用一对中括号
[]
来表示列表(list),用,
分割列表内的每个元素。
l = []
print(l) # []
print(type(l)) # <class 'list'>
print(list('abcdefg')) # ['a', 'b', 'c', 'd', 'e', 'f', 'g']
print(list(range(-1, 4))) # [-1, 0, 1, 2, 3]
print(list(range(1, 11))) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(list(range(1, 11, 3))) # [1, 4, 7, 10]
1.2 列表合并(拼接): +
l1 = [1, 2, 3]
l2 = ['a', 'b', 'c']
l3 = l1 + l2
print(l3) # [1, 2, 3, 'a', 'b', 'c']
合并(拼接)就是将两个列表合为一个新列表,原有列表不变。
1.3 列表的重复:*
l1 = [1, 2, 3]
l2 = l1 * 3
print(l2) # [1, 2, 3, 1, 2, 3, 1, 2, 3]
重复可以理解为将原列表
复制
指定次数,然后相加得到一个新的列表。
1.4 列表成员资格判断:in,not in
l = ['a', 'b', 'we']
print('a' in l) # True
print('w' not in l) # True
print('we' in l) # True
成员资格测试就是判断指定元素是否存在于列表中,存在则返回True,不存在则返回False。
1.5 通过索引取值
l = ['a', 'b', 1, 2]
print(l[0]) # a
print(l[6]) # IndexError: list index out of range
列表中每一个元素都有自己的索引(从0开始)位置,这也是为什么说列表是有序的原因。我们可以通过索引取对应的值。
注意,当通过索引取值时,索引范围超过列表索引长度时,会报错,因为没有这个索引位置。
1.6 切片
l = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
# 从指定索引位置开始取值
print(l[3:]) # ['d', 'e', 'f', 'g']
# 取列表内的一段元素
print(l[3:6]) # ['d', 'e', 'f']
# 每隔两个取一个
print(l[1:6:2]) # ['b', 'd', 'f']
# 取索引为-2的元素
print(l[-2]) # f
# 从左向右取,从0开始,取到倒数第二个之前的
print(l[:-2]) # ['a', 'b', 'c', 'd', 'e']
# 反转列表
print(l[::-1]) # ['g', 'f', 'e', 'd', 'c', 'b', 'a']
切片是根据列表的索引来取值(取范围内的值),需要说明的是,只要是序列类型(字符串,列表,元组),其内的元素都有自己的索引位置,我们可以根据索引位置取值,切片。
1.7 for循环取值
l = ['a', 'b', 'c', 'd']
for i in l:
print(i)
'''
a
b
c
d
'''
for循环取值时,每次循环取出一个元素,然后将这个元素赋值给
i
,我们就可以通过操作i
来操作取出的元素。
1.8 列表元素更新
l = ['a', 'b', 'c', 'd']
print(l, id(l)) # ['a', 'b', 'c', 'd'] 128337600
l[1] = 'w'
print(l, id(l)) # ['a', 'w', 'c', 'd'] 128337600
通过指定的索引修改列表中对应的元素,并且通过打印结果发现,当列表内的元素被修改后,列表的内存地址不变。
1.9 删除列表(元素)
l = ['a', 'b', 'c', 'd']
del l[0]
print(l) # ['b', 'c', 'd']
del l[0], l[1]
print(l) # ['c']
del l
print(l) # NameError: name 'l' is not defined
使用del关键字进行列表元素的删除,del支持删除多个元素。但要注意的是,删除多个元素的时候,需要牢记,要删除的第一个元素后面的元素此时还在索引范围内。
另外,当删除列表中的指定索引后,如果该索引后面还有元素,则后面所有的元素都会往前位移,或者称为补位。
当使用del删除整个列表后,这个列表就不存在了,也就是最后的报错。
还有一点需要注意:
l = ['a', 'b', 'c', 'd']
del l[1], l[2]
print(l, len(l)) # ['a', 'c'] 2
del l[3] # IndexError: list assignment index out of range
原列表的最大索引为3,删除两个元素后,此时列表最大的索引为2,此时却要删除索引为3的元素,就抛出错误了。
1.10 总结,列表中的常用操作符:
操作符(表达式) | 描述 | 重要程度 |
---|---|---|
+ | 合并 | ** |
* | 重复 | ** |
in | 成员资格 | **** |
for i in [1, 2, 3]:print(i) | 迭代 | ***** |
list[2] | 索引取值 | ***** |
list[start:stop:step]、list[::-1] | 切片(截取) | ***** |
列表中的常用方法:
方法 | 描述 | 重要程度 |
---|---|---|
list.append(obj) | 列表添加元素到末尾 | ***** |
list.insert(index,obj) | 列表添加元素到指定位置 | ***** |
list.pop(obj) | 删除列表元素 | ***** |
list.remove() | 删除列表元素 | ***** |
list.reverse() | 反转列表的元素 | **** |
list.sort() | 排序 | *** |
list(seq) | 将序列转换为列表 | ***** |
list.extend(seq) | 列表末尾追加多个值 | *** |
list.count(obj) | 统计某个字符在列表内出现的次数 | **** |
list.index(obj) | 找出指定元素的第一个匹配项的索引位置 | *** |
同样的,还有其他置函数或者声明语句可以应用于列表:
函数 | 描述 | 重要程度 |
---|---|---|
len(list) | 返回列表的元素个数 | ***** |
max(list) | 返回列表内最大的元素 | ** |
min(list) | 返回列表内最小的元素 | ** |
cmp(list1, list2) | 比较两个列表的元素 | ** |
del obj1, obj2 | 删除列表的元素 | **** |
需要注意的,
max()
和min()
函数都需要列表内的元素类型保持一致,不然没法比较啊!
另外,cmp()
函数在Python 3.x中不存在了,如果要使用类似功能,可以使用operator
代替,它适合任何对象:
import operator
print(operator.eq([1, 2], [2, 1])) # False
或者使用更简单的方式:
print([1, 2] > [2, 1]) # False
print([1, 2] == [2, 1]) # False
print([1, 2] != [2, 1]) # True
2. 列表的常用方法
2.1 list.append(obj)
将一个对象(元素)追加到列表的末尾。这只是一个追加操作,并且是原地操作列表,所以该方法没有返回值。
l = ['a', 'b', 'c', 'd']
l.append(1)
print(l) # ['a', 'b', 'c', 'd', 1]
l.append('w')
print(l) # ['a', 'b', 'c', 'd', 1, 'w']
由打印结果可以看到,两个元素都已追加的形式被存放在列表的尾部。
2.2 list.insert(index, obj)
将一个对象(元素)插入到列表的指定索引的前面。这只是一个插入操作,并且是原地操作列表,所以该方法没有返回值。
l = ['a', 'b', 'c', 'd']
l.insert(1, 1)
print(l) # ['a', 1, 'b', 'c', 'd']
可以看到,我们通过insert操作在列表索引为1的元素前面插入一个新元素
1
。
需要注意的是,就如插队一样,前面插入一个人,后面的人都要往后移动一位,如果列表长度很长的话,会对性能有所影响。
In [2]: %timeit list(range(100000)).append('a')
3.25 ms ± 22.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [3]: %timeit list(range(100000)).insert(0, 'a')
3.36 ms ± 99.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
上例,我们通过
ipython
测试同样是十万个元素的列表,一个执行将元素追加到列表的尾部,一个执行将元素插入到列表的头部,可以看到,追加操作需要耗费3.25ms
,而插入操作需要3.36ms
。
2.3 list.pop([obj])
移除列表中的指定索引位置的元素,若不指定索引,默认移除列表尾部的元素,并且将列表返回(返不返回是我的事儿,接不接收是你的事儿)。
l = ['a', 'b', 'c', 'd']
print(l.pop(1)) # b
print(l.pop()) # d
print(l.pop(9)) # IndexError: pop index out of range
print(l.pop('d')) # TypeError: 'str' object cannot be interpreted as an integer
前三个打印好解释,最后一个打告诉我们字符串无法解释为整型,也就是说,
list.pop(obj)
移除操作要么是指定元素下标,要么什么都不指定,按照人家默认的来,而不能指定删除某个元素。
2.4 list.remove(obj)
删除列表中的指定元素,不指定或指定元素不存在报错,而且仅是删除操作,没有返回值。
l = ['a', 'b', 'c', 'd']
l.remove('a')
l.remove('c')
print(l) # ['b', 'd']
l.remove(1) # ValueError: list.remove(x): x not in list
最后报错说删除的指定元素不存在。
2.5 list.reverse()
反转列表,并没有参数和返回值,只是在原地对列表中的元素进行反转。
l = ['a', 'b', 'c', 'd']
l.reverse()
print(l) # ['d', 'c', 'b', 'a']
2.6 list.sort(key=None, reverse=False)
原地对列表进行排序,key是自定义的排序算法,此函数在每次元素比较时被调用,reverse表示排序方式,默认的false是按照升序进行排序的,当reverse=True时,排序结果为降序。
l = ['a', 'c', 'b', 'd']
l.sort()
print(l) # ['a', 'b', 'c', 'd']
l.sort(reverse=True)
print(l) # ['d', 'c', 'b', 'a']
我们来看看key参数怎么使用:
l = ['abc', 'cae', 'edg', 'ffh']
l.sort(key=lambda x: x[1])
print(l) # ['cae', 'abc', 'edg', 'ffh']
通过key指定以每个元素中的索引为1的元素排序。
需要补充的是sorted(key=None, reverse=False)函数,它是Python内置的对于所有序列类型进行排序的函数,而不像sort()方法是列表独有的排序方法。
二者区别是:
- sort方法仅能对列表进行原地排序,没有返回值。
- sorted函数对所有序列类型进行排序,并且并不是原地排序,它会返回排序结果,也就是说我们可以通过一个变量接收排序结果。
需要注意的是,无论使用哪种排序方式,排序对象必须是同类型的,也就是说,如果排序对象是列表,那么列表内的元素都为同一类型,因为不同类型无法比较。
转换为字符串
3. 列表的嵌套
如果嵌套层数过多,可以使用递归的方法,扩展项
前文介绍列表时说的元素类型丰富,就是说列表不仅能存储数字、字符串,还能存储列表。
>>> l = [1, 2, [3, 4]]
>>> for i in l:
... print(i)
...
1
2
[3, 4]
>>> l[2]
[3, 4]
>>> l[2].pop()
4
>>> l
[1, 2, [3]]
>>> l[2].insert(0,'a')
>>> l
[1, 2, ['a', 3]]
可以看到,列表对嵌套部分处理也同样的简单,可以使用我们学过的方法, 不仅如此,列表还可以存储别的数据结构,如字典、元组、集合。
>>> l = [1,(2, 3), [4,[5, 6]], {'a':'b'}, {7, 8}]
>>> for i in l:
... print(i)
...
1
(2, 3)
[4, [5, 6]]
{'a': 'b'}
{8, 7}
>>> l[1][1]
3
>>> l[2][1][1] = 'c'
>>> l[3]['a']
'b'
>>> l[-1]
{8, 7}
>>> l
[1, (2, 3), [4, [5, 'c']], {'a': 'b'}, {8, 7}]
先不管元组、字典、集合的是什么。但并不推荐这么用,因为这样操作起来太不方便,只是演示列表可以各种嵌套,一般使用中,更多的是嵌套一种数据类型,如列表嵌套元组,列表嵌套字典,但很少有同时嵌套元组和字典的。
那么,我们如何展示列表中的所有元素呢?这里我们可以使用嵌套循环完成。
>>> for i in [1, [2, 3], 4]:
... if isinstance(i, list):
... for j in i:
... print(j)
... else:
... print(i)
...
1
2
3
4
上例中,第1行for循环列表,第2行判断每次循环中的元素是否为列表,如果是列表,那么就再用for循环循环打印其内的列表中的元素。否则执行第5行的else语句直接打印列表内的元素。
需要强调的是,Python中并没有二维数组的概念,但是列表嵌套列表同样能够达到相同的目的
4.元组tuple
资料链接:https://www.cnblogs.com/Neeo/articles/11212374.html
Python的元组与列表类似,不同之处在于元组的元素不能修改。
元组使用小括号,列表使用方括号。
元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。
创建空元组
tup1 = ()
元组中只包含一个元素时,需要在元素后面添加逗号
tup1 = (50,)
元组与字符串类似,下标索引从0开始,可以进行截取,组合等。
4.1 元组的基本操作
创建元组
Python中,元组(tuple)用一对小括号
()
表示,元组内的各元素以逗号分隔。
t = ()
print(type(t)) # <type 'tuple'>
t1 = ('name', )
print(t1) # ('name',)
print(type(t1)) # <type 'tuple'>
元组中特别注意逗号的使用,一不小心创建的元组就会成为字符串。
t = ('name')
print(t, type(t)) # ('name', <type 'str'>)
t1 = ('name', )
print(t1, type(t1)) # (('name',), <type 'tuple'>)
索引和切片
元组中索引和切片的用法跟列表和字符串类似,或者取范围内的值,或者可以指定在一段范围内,每几个取一个:
t = ('a', 'b', 'c', 'd', 'e')
# 按照索引取值
print(t[1]) # b
print(t[-1]) # e
# 取两者之间(范围)的值
print(t[0:4]) # ('a', 'b', 'c', 'd')
print(t[0:5:2]) # ('a', 'c', 'e')
# 反转元组,返回反转后的新的元组,原本的元组不变
print(t[::-1]) # ('e', 'd', 'c', 'b', 'a')
# for循环取值
for i in t:
print(i)
'''
a
b
c
d
e
'''
但与列表不同的是,元组身为不可变类型,无法原地修改其中的值。
t = ('a', 'b', 'c', 'd', 'e')
t[2] = 'w'
'''
TypeError: 'tuple' object does not support item assignment
'''
拼接: +
虽然元组无法原地修改其中的元素,但是,通过
+
号可以把两个元组合并为一个新的元组。
t1 = ('a', 'b')
t2 = ('c', 'd', 'e')
t3 = t1 + t2
print(t3) # ('a', 'b', 'c', 'd', 'e')
元组的重复:*
简单来说,正如字符串的重复一样,当对元组使用
*
时,复制指定次数后合并为一个新的元组。
t1 = ('a', 'b')
t2 = t1 * 2
print(t2) # ('a', 'b', 'a', 'b')
成员资格测试: in,not in
与字符串、列表的成员资格测试在元组中同样适用:
t1 = ('a', 'b', 'abc')
print('a' in t1) # True
print('b' not in t1) # False
需要注意的是,成员资格判断,只是会判断某个元素是否存是元组的一级元素,比如上例中的
a
是元组的一级元素。而c
不是元组的一级元素。在元组中,c
是元组一级元素字符串abc
的子串。所以判断结果为False。
序列(元组)类型的打包与解包
t = 1, 2, 3
x, y, z = t
print(x, y, z) # 1 2 3
上例第1行,将1、2、3打包赋值给变量t,相当于将3个苹果打包到一个盒子内。第2行,从盒子t中将3个苹果取出来,分别交给x、y、z,我们称为解包。解包这里需要注意的是,盒子里有几个苹果,必须有几个对应的变量接收。多了不行,少了也不行。
平行赋值
>>> x, y = 1, 2
>>> x,y
(1, 2)
>>> x
1
>>> type(x)
<class 'int'>
>>> a = x,y
>>> a
(1, 2)
>>> type(a)
<class 'tuple'>
如上例第1行所示,平行赋值就是等号右边的1,2分别赋值给等号左边的x,y。第2行就是打包了(只是打包,并没有赋值给某个变量),并且打包后的结果是元组类型。而在第8行将x,y打包并赋值给变量a。此时a就是打包后的元组了。
通过打印斐波那契序列来练习平行赋值:
x, y = 0, 1
while x < 8:
x, y = y, x + y
print(x)
'''
1
1
2
3
5
8
'''
首先定义x,y两个变量并赋值。在每次循环中,x和y值都会重新赋值。
删除元组
注意,这里说的删除仅是删除整个元组,而不是删除元组中某个元素。
>>> t = (1, 2, 3)
>>> del t[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion
>>> del t
>>> t
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 't' is not defined
上例第2行,通过删除元组内的元素导致报错,又一次的证明元组为不可变类型。但我们可以删除整个元组(第6行)。
4.1.1 总结,元组中常用的操作符:
操作符(表达式) | 描述 | 重要程度 |
---|---|---|
+ | 合并 | ** |
* | 重复 | ** |
in | 成员资格 | **** |
for i in (1, 2, 3):print(i) | 迭代 | ***** |
t[2] | 索引取值 | ***** |
t[start:stop:step] | 切片(截取) | ***** |
另外,还有几个内置的函数可以应用于元组:
方法 | 描述 | 重要程度 |
---|---|---|
max(tuple) | 返回元组内最大的元素 | ** |
min(tuple) | 返回元组内最小的元素 | ** |
tuple(seq) | 将序列转换为元组 | ***** |
len(tuple) | 返回元组长度 | ***** |
4.2 元组的嵌套
与列表一样,元组也能嵌套存储数据:
t = (1, (2, 3), [4, [5, 'c']], {'a': 'b'}, {8, 7})
for item in t:
print(item)
'''
1
(2, 3)
[4, [5, 'c']]
{'a': 'b'}
{8, 7}
'''
元组内可以存储的数据类型相当丰富,也可以发现,嵌套元素也会当成一个整体称为元组的子元素。但是我们说元组是不可更改的,但我们发现其中是由列表的,这是怎么回事?
t = (1, (2, 3), [4, [5, 'c']], {'a': 'b'}, {8, 7})
t[0] = 'x' # TypeError: 'tuple' object does not support item assignment
通过上例,可以发现,元组内的普通元素不允许修改,但是列表是可变,我们能否把列表替换为别的元素呢?
t = (1, (2, 3), [4, [5, 'c']], {'a': 'b'}, {8, 7})
print(t[2]) # [4, [5, 'c']]
t[2] = 'w' # TypeError: 'tuple' object does not support item assignment
通过上例也可以发现,如果列表被当成了元组的普通元素,那么它也是不可以修改的,遵循元组不可变特性。但是我们如果试图修改列表中的子元素呢?
t = (1, (2, 3), [4, [5, 'c']], {'a': 'b'}, {8, 7})
t[2][0] = 'w'
print(t) # (1, (2, 3), ['w', [5, 'c']], {'a': 'b'}, {8, 7})
上例,可以发现,
t[2][0]
指的是列表中的第一个元素,我们在第2行修改它,然后发现是可以修改成功的。这是为什么呢?元组内的普通元素不允许修改,嵌套的子元素是否能够修改取决于这个子元素本身属于什么数据类型,如这个子元素是列表,那就可以修改,如果是元组,就可不以修改。那么,了解完元组,你可能更有疑问了,除了不可变之外,元组跟列表没啥区别么?我们通过与列表的对比,来证明存在即是真理!
需要注意的是,元组并没有增、删功能。那么元组如此设计,以放弃增、删为代价换来了什么呢?
性能!是的,换来了性能!不信请看,以下演示代码由IPython(Python的另一个发行版本)完成。
4.3 list列表与元组tuple的对比
4.3.1 list VS tuple:创建(生成)速度
我们通过创建同样大小的list和tuple,来观察有什么变化:
In [1]: % timeit [1, 2, 3, 4, 5]
139 ns ± 2.34 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [2]: % timeit (1, 2, 3, 4, 5)
17.3 ns ± 0.161 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
上例的意思是说我们创建一个长度为5的列表,大概执行了7次大循环,每次大循环中进行
10000000
次小循环。这个7次大循环,每次耗时(平均值)139 ns
,每次(标准)偏差2.34 ns
。而创建同样长度的元组,每次仅耗时17.3 ns
,对比创建列表的耗时139 ns
,可以看到创建元组的速度快很多。
4.3.2 list VS tuple:存储开销
再来看他们的存储开销:
from sys import getsizeof
l = [1, 2, 3, 4, 5]
t = (1, 2, 3, 4, 5)
print(getsizeof(l)) # 56
print(getsizeof(t)) # 48
可以看到,元组在存储空间上面也占优势。
4.3.3 list VS tuple:遍历速度
元组的遍历性能相比列表也是快很多了
4.3.4 list VS tuple:哈希比较
l = [1, 2, 3, 4, 5]
t = (1, 2, 3, 4, 5)
print(hash(t)) # -1883319094
print(hash(l)) # TypeError: unhashable type: 'list'
简单来说,Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
Python中可哈希(hashable)类型:字符串、元组、对象。可哈希类型就是我们常说的不可变类型,优点是性能经过优化,多线程安全,不需要锁,不担心被恶意篡改或者不小心修改了。
不可哈希类型:字典、列表、集合。相对于可哈希类型,使用起来相对灵活。
4.4 为数组添加元组
5. 字典dict
资料链接:https://www.cnblogs.com/Neeo/articles/11212385.html
我们知道,无论字符串、列表、元组都是将数据组织到一个有序的结构中,然后通过下标索引处理数据,这几种数据结构虽然已经满足大多数场景了,但是依然不够丰满,现在了解一种通过名字(key)来处理数据的数据类型,这种名字对应数值的关系我们称之为映射关系,而这种数据类型就是前文或多或少的了解过的——字典(dict)。字典是目前为止,Python唯一的内建的映射类型的数据类型。需要说明的是,从Python 3.6开始,字典元素的存储顺序由各键值对根据存储时的顺序而定(但依然不支持索引取值),并优化了空间上的开销,更节省内存占用。
通过存储同样的数据,利用列表和字典的不同之处来学习字典。比如存储商品的名称和编号,可以用列表来存。
有三个商品,对应三个价钱,那想要知道banana的价钱怎么办?
goods = ['apple', 'orange', 'banana']
price = ['20', '24', '32']
banana = price[goods.index('banana')]
print(banana) # 32
如上例这样取值很麻烦,而且,你可能要问为什么价钱要存成字符串类型?
goods = ['apple', 'orange', 'banana']
price = [20, 24, 32]
banana = price[goods.index('banana')]
print(banana) # 32
上例虽然可以存储为int类型。但要是存个电话号码呢?比如存010998998
这样的数字怎么存?
number = [010, 998, 998] # SyntaxError: invalid token
这个问题告诉我们,碰到类似的数据,或者是0开头的数据,尽量存储为字符串类型,而不是整型。另外,虽然列表能存储多种数据类型,但是通过上面的商品和价钱的例子,在用列表的时候,应该尽量存储数据为单一的类型。
好,言归正传,再来看看更好的选择。
goods = {'apple': '20', 'orange': '24', 'banana': '32'}
print(goods['banana']) # 20
上例采用了字典来存储数据,接下来就学习一下字典的用法。
5.1 字典基本操作
字典详细讲解:https://www.cnblogs.com/Neeo/articles/11212385.html
字典是无序(Python 3.6版本之前)的可变类型数据类型。我们通过一对
{}
来创建字典(dict),字典内的值是通过:
来存储的,也就是key:value
的格式。
d1 = {}
d2 = dict()
print(d1, d2) # {} {}
d3 = {'apple': 20}
print(d3) # {'apple': 20}
print(d3['apple']) # 20
5.1.1 使用内建函数dict()来创建字典
d = dict(apple='20', orange='24')
print(d) # {'apple': '20', 'orange': '24'}
5.1.2 字典的增删改查
d = dict(apple=20, orange=24)
print(d['apple']) # 字典取值
d['apple'] = 40 # 为字典的apple键重新赋值
print(d)
d['banana'] = 32 # 为字典新添加一对键值对
print(d)
del d['apple']
del d['orange'], d['banana']
del d
我们可以通过字典加中括号的形式,来对字典进行增删改查的操作。但需要注意的是,通过中括号取值的时候和
del
声明删除字典的key时,该key必须存在,否则报错(KeyError)。
列表的成员资格测试是不稳定的,而字典稳定。这是因为列表的成员资格判断取决于判断元素处于列表的什么位置,如果是列表的靠前位置,速度肯定快,如果是末尾,就要从头到尾挨着判断,速度就慢下来了。
而字典的存储结构是由key
、哈希表
、value
三部分组成的,字典在创建一个键值对的时候,首先将key通过哈希得到结果,结果的位置上就是value,然后这个对应关系被保存在哈希表上。查询的时候,无论该key在何方,只要哈希后根据结果去哈希表上就能找到对应的value。所以查询速度快而且稳定,但由于要维护哈希表,在存储和修改时可能会相对麻烦些。
不是什么元组都能成为字典的key
需要注意的是,如果字典的key是元组,那么这个元组就不能是普通的元组了。我们知道元组是可哈希的数据类型,这种说法是基于元组内的元素都是不可变类型才成立的。
print(hash((1, 'a', 2.3, (4, 5)))) # -1680772126
t1 = (1, 'a', 2.3, (4, 5), [6, 7])
print(t1) # (1, 'a', 2.3, (4, 5), [6, 7])
print(hash(t1)) # TypeError: unhashable type: 'list'
5.2 字典的常用操作
遍历字典
d = {'a': 1, 'b': 2, 'c': 3}
for k in d:
print(k)
'''
a
b
c
'''
由上例可以看到,遍历的时候,字典默认返回key,那么怎么分别取key和value呢?
d = {'a': 1, 'b': 2, 'c': 3}
print(d.keys()) # dict_keys(['a', 'b', 'c'])
for k in d.keys():
print(k)
'''
a
b
c
'''
print(d.values()) # dict_values([1, 2, 3])
for k in d.values():
print(k)
'''
1
2
3
'''
上例中,d.keys()
取出来的是字典所有的key,而d.values()
取出的是所有的value。
那怎么同时取key和value呢:
d = {'a': 1, 'b': 2, 'c': 3}
for k in d.items():
print(k)
'''
('a', 1)
('b', 2)
('c', 3)
'''
for k, v in d.items():
print(k, v)
'''
a 1
b 2
c 3
'''
第一个for循环,我们通过d.items()
以元组的形式拿到key和value。第二个循环,相当于平行赋值,每次从d.items()
中取出一个元组,然后平行赋值给k v
两个变量。
dict.get(key, None)
之前说,我们通过字典加中括号取值的时候,当key不存在的时候,会报错:
d = {'a': 1, 'b': 2, 'c': 3}
print(d['a']) # 1
print(d['w']) # KeyError: 'w'
报错~嗯2333333,很明显是不行滴,怎么办呢,我们可以通过dict.get()
方法来避免这个问题:
d = {'a': 1, 'b': 2, 'c': 3}
print(d['a']) # 1
print(d.get('a')) # 1
print(d.get('w')) # None
print(d.get('w', 'hi man, 你想要的键不存在呀')) # hi man, 你想要的键不存在呀
dict.get()
可以获取指定的key,当key不存在的时候,默认会返回一个None
,意思是没取到key,而且我们可以修改这个返回值,更加灵活。
dict.setdefault(key, None)
d = {'a': 1, 'b': 2, 'c': 3}
print(d.setdefault('a')) # 1
print(d.setdefault('x', 'y')) # y
print(d) # {'a': 1, 'b': 2, 'c': 3, 'x': 'y'}
dict.setdefault()
方法跟dict.get()
方法类似,获取字典指定的key,存在则返回对应的value,如果key不存在的话,它会将我们指定的key添加到字典中,默认的value是None,该value也可以自己指定。
dict.update(obj)
字典可以拼接(+)吗?
d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'd': 4}
d3 = d1 + d2 # TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
显然是不行的!
但我们可以通过dict.upate()
方法来完成类似的操作:
d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'd': 4}
d2.update(d1)
print(d2) # {'c': 3, 'd': 4, 'a': 1, 'b': 2}
此方法将一个字典(d1)的键值对一次性的更新到当前字典(d2),如果两个字典中存在同样的键,更新的时候,对应的值以另一个字典(d1)的值为准。
注意,字典的键是唯一的,但是值可以是相同的。
dict.pop() & dict.popitem()
我们在上面也了解过了del来删除字典以及删除字典的键,这里我们再介绍两个方法删除字典的键。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
print(d.pop('a'), d) # 1 {'b': 2, 'c': 3, 'd': 4}
# print(d.pop('w')) # KeyError: 'w'
print(d.popitem()) # ('d', 4)
d.pop()
方法删除字典指定的key,返回对应的value,如果key不存在,则抛出KeyError
错误。
而d.popitem()
在Python2.7(Python 2.x的最后一个版本)和Python3.6(及以上版本)删除的是最后一个key。而其他版本(如Python2.4和Python3.5版本)则是随机删除一对键值对。另外,如果字典为空,上述两个方法都将抛出KeyError
的错误。
dict.clear()
要清空一个字典,使用dict.clear()
方法,这没啥好解释的:
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
d.clear()
print(d) # {}
dict.fromkeys()
d = dict.fromkeys(['a', 'b'], None)
print(d) # {'a': None, 'b': None}
d1 = dict.fromkeys(['a', 'b'], '自定义value')
print(d1) # {'a': '自定义value', 'b': '自定义value'}
dict.fromkeys(seq, None)
用来生成字典 ,第一个参数seq为序列类型(如上例中的字符串,列表)作为字典的key,第二个参数为每一个key对应的value,如若不设置则默认为None。
dict.contains(key)
dict.__contains__(key)
用来判断某一个key是否在字典中。
d = dict.fromkeys(['a', 'b'], None)
print(d.__contains__('a')) # True
需要注意的是,dict.__contains__(key)
适用于Python2和3中,而Python2中,多了一个d.has_key(key)
的方法,同样用来判断key是否在字典中。但这方法在Python3中取消了。
d = dict.fromkeys(['a', 'b'], None)
print(d.__contains__('a')) # True
print(d.has_key('b')) # True
小结 字典中的常用方法或函数或者声明语句:
方法 | 描述 | 重要程度 |
---|---|---|
x in dict | 成员运算符 | *** |
help(dict) | 获取字典的帮助信息 | ***** |
len(dict) | 返回字典项目的个数 | ***** |
str(dict) | 转字典类型为字符串类型 | **** |
type(dict) | 查看类型 | **** |
sorted(dict) | 字典排序 | ***** |
dict.fromkeys(seq) | 创建新字典 | ***** |
dict1.update(dict2) | 另一个字典更新当前字典 | ***** |
dict.keys () | 以列表的形式返回字典的所有的键 | ***** |
dict.values() | 以列表的形式返回字典的所有的值 | ***** |
dict.setdefault(key,default=None) | 返回字典的键,如不存在则添加为字典的key,value则默认None | *** |
dict.popitem() | 随机删除字典中的键值对 | *** |
dict.pop() | 删除字典中的指定key,并返回对应的value | ***** |
del dict | 删除字典或其内的指定键值 | *** |
dict.clear() | 将字典清空 | *** |
6. 漫谈爬虫
小实例:爬取校花网
'''
pip install requests
pip install BeautifulSoup4 # 下载BeautifulSoup4
'''
import requests
from bs4 import BeautifulSoup
response = requests.get(url='http://www.xiaohuar.com/hua/')
soup_obj = BeautifulSoup(response.text, 'html.parser')
div_obj = soup_obj.find(name='div', attrs={'class': 'demo clearfix'})
img_list = div_obj.find_all(name='img')
for item in img_list:
item_url = 'http://www.xiaohuar.com' + item.get('src')
img_path = item.get('alt') + '.' + item_url.rsplit('.')[-1]
img_response = requests.get(item_url)
f = open('./校花网图片/{}'.format(img_path), 'wb')
f.write(img_response.content)
f.close()
print('{} 下载完毕...'.format(img_path))
小坑:下载BeautifulSoup4不成功
7.今日作业
昨日作业最后的四道题
11.写代码,有如下列表,按照要求实现每一个功能
li = ["alex", "WuSir", "ritian", "barry", "wenzhou"]
计算列表的长度并输出
列表中追加元素"seven",并输出添加后的列表
请在列表的第1个位置插入元素"Tony",并输出添加后的列表
请修改列表第2个位置的元素为"Kelly",并输出修改后的列表
请将列表l2=[1,"a",3,4,"heart"]的每一个元素添加到列表li中,一行代码实现,不允许循环添加。
请将字符串s = "qwert"的每一个元素添加到列表li中,一行代码实现,不允许循环添加。
请删除列表中的元素"ritian",并输出添加后的列表
请删除列表中的第2个元素,并输出删除的元素和删除元素后的列表
请删除列表中的第2至4个元素,并输出删除元素后的列表
请将列表所有得元素反转,并输出反转后的列表
请计算出"alex"元素在列表li中出现的次数,并输出该次数。
12.写代码,有如下列表,利用切片实现每一个功能
li = [1, 3, 2, "a", 4, "b", 5,"c"]
通过对li列表的切片形成新的列表l1,l1 = [1,3,2]
通过对li列表的切片形成新的列表l2,l2 = ["a",4,"b"]
通过对li列表的切片形成新的列表l3,l3 = ["1,2,4,5]
通过对li列表的切片形成新的列表l4,l4 = [3,"a","b"]
通过对li列表的切片形成新的列表l5,l5 = ["c"]
通过对li列表的切片形成新的列表l6,l6 = ["b","a",3]
13.写代码,有如下列表,按照要求实现每一个功能。
lis = [2, 3, "k", ["qwe", 20, ["k1", ["tt", 3, "1"]], 89], "ab", "adv"]
将列表lis中的"tt"变成大写。
将列表中的数字3变成字符串"100"。
将列表中的字符串"1"变成数字101。
14.请用代码实现:利用下划线将列表的每一个元素拼接成字符串
li = ["alex", "wusir", "taibai"]
利用下划线将列表的每一个元素拼接成字符串"alex_wusir_taibai"