玩转python字典
介绍
我们知道列表是存放数据的容器,需要时我们通过索引(下标)取出数据元素。但是这种取值方式显得不那么友好,我们需要记住数据在列表中的位置才能顺利取值。这种情况下,python给我们提供了另一种基本数据类型,方便我们取值,它就是字典(dict)。
字典是由多个 键值对 组成,键(key)是数据的别称,值(value)才是我们需要的数据。此时,我们取数据不需要记住数据存放的顺序,直接报数据的别名就可以取出需要的数据。这种取值方式更加简洁、人性化。
定义字典
# 方式一:
# user = {"键名1": "值名1", "键名2": "值名2", ......}
>>> {"name": "jack", "age": 18}
{'name': 'jack', 'age': 18}
>>> d = {} # 定义空字典
>>> d
{}
# 方式二:
# user = dict(one=1, two=2)
>>> dict(name='jack',age=18)
{'name': 'jack', 'age': 18}
# 方式三:
info=[
['name','egon'],
('age',18),
['gender','male']
]
res=dict(info)
# 循环本质:
d={}
for k,v in info: # k,v=['name','egon'],
d[k]=v
print(d)
# 方式四:快速初始化一个字典
keys=['name','age','gender']
d={}.fromkeys(keys,None)
# fromkeys本质循环赋值, 注意如果此时None换为[],字是浅拷贝,导致的结果是每个key对应的value都是一块形同的内存地址
d={}
for k in keys:
d[k]=None
补充:
- 字典的键必须是不可变数据类型,如int,str, tuple等,并且当tuple包含可变数据类型时也不可以做键;
- 字典的值可以是任意数据类型。
- 字典的键是唯一的,一个字典中,键不能重复;值允许重复。
字典基本操作
字典不支持索引取值和切片
# 在字典中键值对,不能像字符串和列表那样通过索引取值,因此也不支持切片操作。
# 字典不支持索引
>>> user = {"name": "jack", "age": 18}
>>> user[0]
Traceback (most recent call last):
File "<pyshell#33>", line 1, in <module>
user[0]
KeyError: 0
# 字典不支持切片
>>> user[0:2]
Traceback (most recent call last):
File "<pyshell#34>", line 1, in <module>
user[0:2]
TypeError: unhashable type: 'slice'
字典通过键取值
# 字典取值直接通过键的方式获取,如果键不存在,则报错。
>>> user['name']
'jack'
修改字典的值
# 修改字典的值,也是通过key的方式
>>> user = {"name": "jack", "age": 18}
>>> user['name']='cindy'
>>> user
{'name': 'cindy', 'age': 18}
字典添加键值对
# 往字典中添加新的键值对,很简单
>>> user = {"name": "jack", "age": 18}
>>> user['sex']='male'
>>> user
{'name': 'jack', 'age': 18, 'sex': 'male'}
删除字典的key-value
# 删除字典的键值对也是通过key的方式
>>> user = {"name": "jack", "age": 18, 'sex': 'male'}
>>> del user['sex']
>>> user
{'name': 'jack', 'age': 18}
del 删除字典、len 获取字典的长度
# 字典的长度,即字典中键的个数
>>> user = {"name": "jack", "age": 18, 'sex': 'male'}
>>> len(user)
3
>>> del user
>>> user
Traceback (most recent call last):
File "<pyshell#46>", line 1, in <module>
user
NameError: name 'user' is not defined
in、not in 判断key是否在字典中
# 字典的长度,即字典中键的个数
>>> user = {"name": "jack", "age": 18, 'sex': 'male'}
>>> 'name' in user
True
循环遍历字典
# 使用for询函遍历字典,遍历的是字典的key
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> for i in user:
print(i, end=',')
name,age,sex,
总结:
- 字典不支持索引和切片;
- 字典中,获取、修改、删除数据都需要通过键;
- 字典的长度是键的个数,遍历字典是遍历字典的键。
内置方法
常用内置方法
get(key, default=None) 返回key对应的value,或者default。程序不会报错
# get()方法获取key对应的value值;
# 并且可以指定如果key不存在时的返回值,默认key不存在什么都不返回。
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> user.get('name')
'jack'
>>> user.get('height') # key不存在时,返回None
>>> user.get('height', 'height找不到') # 指定返回值
'height找不到'
keys()
# python2中 直接返回一个列表,列表中包含字典的所有key
# python3中,返回一个生成器,循环它时逐个返回字典的key
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> for i in user.keys():
print(i, end=',')
name,age,sex,
values()
# 与keys类似,返回的是包含values的生成器
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> for i in user.values():
print(i, end=',')
jack,18,male,
items()
# 与keys、values类似同时返回key和value, 返回的是列表,列表的每个元素是由键和值组成的2元祖。
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> for i in user.items():
print(i, end=',')
('name', 'jack'),('age', 18),('sex', 'male'),
# 可以采用解压赋值的方式直接获取key和value
>>> for k, v in user.items():
print(k, v, sep=',')
name,jack
age,18
sex,male
pop(key, default) 通过key删除键值对并返回值,key不存在则返回default(无default则报错)
# 使用key删除字典中的键值对,并返回key对应的value。
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> user.pop('sex')
'male'
# 如果没有使用可选参数default,key不存在时程序会报错。
>>> user.pop('sex')
Traceback (most recent call last):
File "<pyshell#84>", line 1, in <module>
user.pop('sex')
KeyError: 'sex'
# 为了避免key不存在而报错,使用可选参数default,自定制返回值
>>> user.pop('sex', 'no sex')
'no sex'
其他内置方法
popitem() 随机删除字典一个键值对,并以2元祖的返回键和值。如果字典为空则报错。
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> user.popitem()
('sex', 'male')
# 这是因为数据少,看不出来随机删除的效果。
clear()
# 删除字典内所有键值对,字典依然还存在。与列表的clear()相同。
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> user.clear()
>>> user
{}
copy() 浅拷贝一个新的字典
# 拷贝新字典并返回
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> user.copy()
{'name': 'jack', 'age': 18, 'sex': 'male'}
# 浅拷贝的概念日后再说
setdefault(key, default) 有key则返回value,无key则新增key-default并返回default
# 与get()功能类似,通过key获取value
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> user.setdefault('name')
'jack'
# 当key不存在,不提供value,此时添加一个key,value=None
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> user.setdefault('height')
>>> user
{'name': 'jack', 'age': 18, 'sex': 'male', 'height': None}
# 当key不存在,提供value,此时添加一个key,value=default,并返回default
>>> user = {"name": "jack", "age": 18, 'sex':'male'}
>>> user.setdefault('height', 175)
175
>>> user
{'name': 'jack', 'age': 18, 'sex': 'male', 'height': 175}
# 通过setdefault()无法修改value
>>> user.setdefault('height', 180)
175
>>> user
{'name': 'jack', 'age': 18, 'sex': 'male', 'height': 175}
fromkeys(iterable, value=None) 字典批处理赋值,有去重作用
# fromkeys的作用:批处理定义一个包含多个键,每个键的值相同的字典
>>> names = ['jack', 'cindy', 'sam']
>>> scores = {}
>>> scores.fromkeys(names, 100)
{'jack': 100, 'cindy': 100, 'sam': 100}
# python源码中,fromkeys是被staticmethod 装饰的函数。故dict也可以调用,这样更方便
>>> scores = dict.fromkeys(names, 100)
>>> scores
{'jack': 100, 'cindy': 100, 'sam': 100}
# 注意:利用 fromkeys 可以给列表去重。 list(dict.fromkeys(my_list))
#注意坑:如果参数value是列表或字典等可变数据类型,每个键都指向相同的内存地址,一个值改变所有键对应的值都改变。
update(E, **F) 将其他字典的键值对添加到该字典中,如果键相同则值替换为新的值
update(self, E=None, **F):
"""
D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]
If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v
In either case, this is followed by: for k in F: D[k] = F[k]
"""
# case1:如果只有F
>>> d = {'a':1, 'b':2}
>>> f = {'a':2, 'c':3}
>>> d.update(f) # 没有的新增键值对,已有的更新值
>>> d
{'a': 2, 'b': 2, 'c': 3}
# case2:有E(E是字典),F(F是字典)
>>> d = {'a':1, 'b':2}
>>> f = {'a':2, 'c':3}
>>> e = {'d':4, 'e':5}
>>> d.update(e, f) # 参数不对,报错
Traceback (most recent call last):
File "<pyshell#207>", line 1, in <module>
d.update(e, f)
TypeError: update expected at most 1 arguments, got 2
>>> d.update(e, **f) # 参数正确
>>> d
{'a': 2, 'b': 2, 'd': 4, 'e': 5, 'c': 3}
# case3:有E(E不是字典,E的元素必须是二元组)
>>> d = {'a':1, 'b':2}
>>> e = [(1,2),(3,4)] # 元素必须是二元组才能有循环 for k, v in E:
>>> d.update(e)
>>> d
{'a': 1, 'b': 2, 1: 2, 3: 4}
练习
练习1: 有如下值集合 [11,22,33,44,55,66,77,88,99,90...],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。即: {'k1': 大于66的所有值, 'k2': 小于66的所有值}。
练习2:统计s='hello alex alex say hello sb sb'中每个单词的个数。结果如:{'hello': 2, 'alex': 2, 'say': 1, 'sb': 2}
练习3:三级菜单,要求打印 省市县三级菜单,可随时返回上一级和退出程序。
补充
dict的key是有序的?
python3.5(含)以前是不能保证先添加的键值对先循环打印出来;
python3.6后,对字典的底层存储做了改进,支持先添加的键值对,先循环打印。
有序字典 OrderedDict
OrderedDict 是python内部collections模块提供的一个类,OrderedDict 继承dict。
OrderedDict 支持键插入的顺序。python3.6以后普通字典已经支持字典key的有序化。但是OrderedDict 依然有它的特殊之处,特定的时候使用方便。
1-创建有序字典
from collections import OrderedDict # 引入OrderedDict类
order_dict = OrderedDict() # 实例化order_dict(有序字典对象)
for i in range(5): # 循环插入键值对
order_dict[i] = i*10
print(order_dict) # OrderedDict([(0, 0), (1, 10), (2, 20), (3, 30), (4, 40)])
2-获取有序字典的key
print(order_dict.keys()) # odict_keys([0, 1, 2, 3, 4])
for key in order_dict.keys(): # 0 1 2 3 4
print(key, end=' ')
3-获取有序字典的value
print(order_dict.values()) # odict_values([0, 10, 20, 30, 40])
for value in order_dict.values(): # 0 10 20 30 40
print(value, end=' ')
4-同时获取key-value
print(order_dict.items())
for k, v in order_dict.items():
print((k, v), end=' ')
# output:
# odict_items([(0, 0), (1, 10), (2, 20), (3, 30), (4, 40)])
# (0, 0) (1, 10) (2, 20) (3, 30) (4, 40)
5-pop(key, default)
# 同dict的pop(key, default)方法相同
# 如果key存在则删除key对应的键值对,并返回value
# 如果key不存在,则返回default的值,default没有指定的报错。
6-copy() & clear()
# # 同dict的copy()、clear()方法相同。复制和清空有序字典
7-setdefault(key, default)
# 同dict的setdefault()方法相同
# 如果key不在字典中,插入key-default键值对,并返回default,无default则返回None
# 如果key在字典中,则返回key对应的value值
8-fromkeys(cls, iterable, value=None)
# 同dict的fromkeys方法相同,都是静态方法,类和对象都可以调用
# 将一个可迭代对象的元素当做字典的key,value是初始值
# 记得坑:如果value是一个可变数据类型。修改字典中的任何一个value,则所有的value都相应变化
上述方法具有Dict的对应的方法在使用方式和功能上完全一样。
下面两个方法是OrderedDict的特殊方法。
9-popitem(self, last=True)
# OrderedDict的popitem与dict.popitem()方法不同
# 有序字典的popitem 删删除一个键值对并返回键值对,可以按照后进先出的顺序;也可以按照先进先出的顺序删除
# last=Ture(默认), 后进先出LIFO
for _ in range(5):
print(order_dict.popitem(), order_dict)
# 输出:
# (4, 40) OrderedDict([(0, 0), (1, 10), (2, 20), (3, 30)])
# (3, 30) OrderedDict([(0, 0), (1, 10), (2, 20)])
# (2, 20) OrderedDict([(0, 0), (1, 10)])
# (1, 10) OrderedDict([(0, 0)])
# (0, 0) OrderedDict()
# last=False, 先进先出FIFO
for _ in range(5):
print(order_dict.popitem(last=False), order_dict)
# 输出
# (0, 0) OrderedDict([(1, 10), (2, 20), (3, 30), (4, 40)])
# (1, 10) OrderedDict([(2, 20), (3, 30), (4, 40)])
# (2, 20) OrderedDict([(3, 30), (4, 40)])
# (3, 30) OrderedDict([(4, 40)])
# (4, 40) OrderedDict()
10-move_to_end(self, key, last=True)
# move-to_end是OrderedDict的特殊方法
'''Move an existing element to the end (or beginning if last is false).
Raise KeyError if the element does not exist.
'''
# 如果key存在, 默认将key-value移到最后一位,也可以移到第一位(last=False)
# 如果key不存在则报错
print('移动前:', order_dict)
order_dict.move_to_end(2) # 将(2, 20)移到尾部
print('移到尾部:', order_dict)
order_dict.move_to_end(3, last=False) # 将(3,30)移到头部
print('移到头部:', order_dict)
# 输出:
移动前: OrderedDict([(0, 0), (1, 10), (2, 20), (3, 30), (4, 40)])
移到尾部: OrderedDict([(0, 0), (1, 10), (3, 30), (4, 40), (2, 20)])
移到头部: OrderedDict([(3, 30), (0, 0), (1, 10), (4, 40), (2, 20)])