Python 1-18 字典
Python 的字典数据类型采用键值对 (key:value) 的形式,根据 key 的值计算 value 的地址,具有非常快的查取和插入速度。
例如,用 list 实现成绩单:
# 给定一个名字,要查找对应的成绩,就先要在 names 中找到对应的位置,再从 scores 取出对应的成绩,list 越长,耗时越长。
names = ['张三', '李四', '王五']
scores = [95, 75, 85]
name = '李四'
try:
print(f'{name}:{scores[names.index(name)]}')
except Exception as e:
print(e)
如果用 dict 实现,只需要一个“名字”-“成绩”的对照表,直接根据名字查找成绩,查找速度很快。
>>> d = {'张三': 95, '李四': 75, '王五': 85}
>>> d['李四']
75
为什么 dict 查找速度比 list 快?因为 dict 的实现原理和查新华字典是一样。
在 list 中查找元素的方法是从头往后一个一个查,list 越大,查找越慢。
在 dict 中可以直接计算出 “李四” 对应的存放成绩 95 的内存地址,直接取出来,所以速度非常快。
list:查找和插入的时间随着元素的增加而增加;占用空间小。
dict:查找和插入的速度极快,不会随着 key 的增加而变慢;需要占用大量的内存。
dict 是用空间来换取时间的一种方法。
注意:从Python3.6开始,字典是有序的!它将保持元素插入时的先后顺序!
字典格式:
d = {key1 : value1, key2 : value2 }
1、创建字典
# class dict(**kwarg)
# class dict(mapping, **kwarg)
# class dict(iterable, **kwarg)
>>>dict() # 创建空字典
{}
>>> type(dict) # <class 'dict'>
>>> dict(a=1, b=2, t=3) # 传入关键字
{'a': 1, 'b': 2, 't': 3}
>>> dict(zip(['one', 'two', 'three'], [1, 2, 3])) # 映射函数方式来构造字典
{'one': 1, 'two': 2, 'three': 3}
>>> dict([('one', 1), ('two', 2), ('three', 3)]) # 可迭代对象方式来构造字典
{'one': 1, 'two': 2, 'three': 3}
2、访问字典
虽然现在的字典在访问时有序了,但字典依然是集合类型,不是序列类型,因此没有索引下标的概念,更没有切片的说法。
但与 list 类似的地方是,字典采用把相应的键放入方括号内获取对应值的方式取值。
dic = {'Name': 'Python', 'Age': 7, 'Class': 'First'}
print("dic['Name']: ", dic['Name'])
如果用字典里没有的键访问数据,会输出错误如下:
print ("dict['Alice']: ", dic['Alice']) # KeyError: 'Alice'
要避免 key 不存在的错误,有两种办法,一是通过 in 判断 key 是否存在:
'Thomas' in dic # False
二是通过 dict 提供的 get() 方法,如果 key 不存在,可以返回 None,或者自己指定的 value:
dic.get('Thomas',default=None)
dic.get('Thomas', -1) # -1
注意:返回 None 的时候 Python 的交互环境不显示结果。
3、增加和修改
由于一个 key 只能对应一个值,所以,多次对一个 key 赋值,后面的值会把前面的值覆盖掉。
dic = {'Name': 'Python', 'Age': 7, 'Class': 'First'}
dic['Age'] = 8 # 更新 Age
dic['School'] = "Python" # 添加信息
print (dic)
4、删除字典元素、清空字典和删除字典
使用 del 关键字删除字典元素或者字典本身,使用字典的 clear() 方法清空字典。
dic = {'Name': 'Python', 'Age': 7, 'Class': 'First'}
del dic['Name'] # 删除键 'Name'
dic.clear() # 清空字典
del dic # 删除字典
print ("dic['Age']: ", dic['Age'])
但这会引发一个异常,因为用执行 del 操作后字典不再存在:
Traceback (most recent call last):
File "test.py", line 9, in <module>
print ("dic['Age']: ", dic['Age'])
TypeError: 'type' object is not subscriptable
要删除一个 key,用 pop(key) 方法,对应的 value 也会从 dict 中删除:
>>> dic = {'Name': 'Python', 'Age': 7, 'Class': 'First'}
>>> dic.pop('Age')
7
5、字典键的特性
键必须是唯一的、不可变的,键可以是字符串,数字或元组,值可以取任何数据类型。
1)不允许同一个键出现两次。创建时如果同一个键被赋值两次,后一个值会被记住:
dic = {'Name': 'Python', 'Age': 7, 'Name': '小菜鸟'}
print ("dic['Name']: ", dic['Name'])
2)键必须不可变,所以可以用数字,字符串或元组充当,而用列表就不行:
dic = {['Name']: 'Python', 'Age': 7}
print ("dic['Name']: ", dic['Name']) # TypeError: unhashable type: 'list'
6、字典内置函数 & 方法
len(dict) # 计算字典元素个数,即键的总数。
str(dict) # 输出字典,以可打印的字符串表示。
>>> dict = {'Name': 'Python', 'Age': 7, 'Class': 'First'}
>>> str(dict) # "{'Name': 'Python', 'Class': 'First', 'Age': 7}"
Python字典包含了以下内置方法:
dict.get(key, default=None) # 返回指定键的值,如果键不在字典中返回 default 值
dict.setdefault(key, default=None) # 和 get() 类似, 但如果键不存在于字典中,将会添加键并将值设为 default
dict.pop(key[,default]) # 删除字典给定键及其所对应的值,返回被删除键的值。如果键不存在,返回 default 值。
dict.popitem() # 返回(键,值)元组并删除字典中的最后一对键和值。
dict.clear() # 删除字典内所有元素
dict.copy() # 返回一个字典的浅复制
dict.fromkeys(seq[, value]) # 创建一个新字典,以序列 seq 中元素做字典的键,value为字典所有键对应的初始值。
dict.update(dict2) # 把字典 dict2 的键/值对更新到 dict 里
dict.items() # 返回一个迭代器,返回可遍历的(键, 值) 元组列表
dict.keys() # 返回一个迭代器,返回可遍历的键列表
dict.values() # 返回一个迭代器,返回可遍历的值列表
7、遍历字典
从 Python3.6 开始遍历字典获得的键值对是有序的。
dic = {'Name': 'Jack', 'Age': 7, 'Class': 'First'}
# 1 直接遍历字典获取键,根据键取值
for key in dic:
print(key, dic[key])
# 2 利用keys方法获取键
for key in dic.keys():
print(key, dic[key])
# 3 利用items方法获取键值,速度很慢,少用!
for key,value in dic.items():
print(key,value)
#4 利用values方法获取值,但无法获取对应的键。
for value in dic.values():
print(value)
8、综合展示
看下面的例子,其中有一些是错误做法展示:
>>> dic = {}
>>> for i in 'adilwste':
... dic[i] = ord(i)
...
>>> dic
{'a': 97, 'd': 100, 'i': 105, 'l': 108, 'w': 119, 's': 115, 't': 116, 'e': 101}
>>> for i in dic:
... print(i,dic[i])
...
a 97
d 100
i 105
l 108
w 119
s 115
t 116
e 101
>>> dic[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 0
>>> dic.pop()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: pop expected at least 1 argument, got 0
>>> dic.popitem()
('e', 101)
>>> dic
{'a': 97, 'd': 100, 'i': 105, 'l': 108, 'w': 119, 's': 115, 't': 116}
>>> dic.popitem('d')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: popitem() takes no arguments (1 given)
>>> a = dic.popitem()
>>> a
('t', 116)
Python 直接赋值、浅拷贝和深度拷贝
直接赋值:就是对象的引用(别名)。
浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。即内容子对象引用相同。
深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
字典浅拷贝:
>>> a = {1: [1,2]}
>>> b = a.copy()
>>> a, b
({1: [1, 2]}, {1: [1, 2]})
>>> a[1].append(3)
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
深度拷贝需要引入 copy 模块:
>>> import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3]})
解析
1、b = a: 赋值引用,a 和 b 都指向同一个对象。
2、b = a.copy(): 浅拷贝, a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(是引用)。
3、b = copy.deepcopy(a): 深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的。
以下实例是使用 copy 模块的 copy.copy( 浅拷贝 )和(copy.deepcopy ):
import copy
a = [1, 2, ['a', 'b']] #原始对象
b = a #赋值,传对象的引用
c = copy.copy(a) #对象拷贝,浅拷贝
d = copy.deepcopy(a) #对象拷贝,深拷贝
a.append(3) #修改对象a
a[2].append('c') #修改对象a中的['a', 'b']数组对象
print( 'a = ', a )
print( 'b = ', b )
print( 'c = ', c )
print( 'd = ', d )
以上实例执行输出结果为:
a = [1, 2, ['a', 'b', 'c'], 3]
b = [1, 2, ['a', 'b', 'c'], 3]
c = [1, 2, ['a', 'b', 'c']]
d = [1, 2, ['a', 'b']]