Python 中的dict类提供了许多处理字典的实用方法。其中一些已经在dicts的概念中介绍过。这里我们再介绍几个,以及一些遍历和操作字典的技巧。
setdefault()实现无错误插入字典概念之前已经讲过,.get(key, <default value>)会返回已有的value;如果字典中找不到某个key,就返回default value,从而避免KeyError。当你不希望添加额外的错误处理,又无法确定要找的key一定存在时,这种方式就很好用。
对于同样“安全”的插入操作(不会引发KeyError),可以使用.setdefault(key, <default value>)方法。如果key能在字典中找到,setdefault(key, <default value>)会返回对应的value;如果没有找到该键,它会 插入(key, default value)键值对,并返回default value供使用。
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
# Looking for the value associated with key "Rock Brown".
# The key does not exist, so it is added with the default value, and the value is returned.
>>> palette_I.setdefault('Rock Brown', '#694605')
'#694605'
# The (key, default value) pair has now been added to the dictionary.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
fromkeys()从可迭代对象填充字典要用各种keys和默认值快速填充字典,可以使用 类方法fromkeys(iterable, <default value>),它会遍历由keys组成的可迭代对象并创建一个新的dict。所有values都会被设置为所提供的default value:
>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
{'Grassy Green': 'fill in hex color here',
'Purple Mountains Majesty': 'fill in hex color here',
'Misty Mountain Pink': 'fill in hex color here'}
.keys()、.values()和.items()方法会返回字典的可迭代视图。
借助这些视图,可以轻松地遍历条目而不修改它们。视图也是 动态的:当底层字典数据发生变化时,关联的view object也会随之变化:
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
# Using .keys() returns a list of keys.
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
# Using .values() returns a list of values.
>>> palette_I.values()
dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
# Using .items() returns a list of (key, value) tuples.
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
# Views are dynamic. Changing values in the dict changes all of the associated views.
>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
>>> palette_I['Deep Red'] = '#932432'
>>> palette_I.values()
dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
.keys()、.values()和.items()
在 Python 3.7+ 中,dicts会保留条目的插入顺序,因此在使用.keys()、.values()或.items()时可以按先进先出(FIFO)的顺序迭代。
在 Python 3.8+ 中,视图也是 可逆的。这样就能通过reversed(<dict>.keys())、reversed(<dict>.values())或reversed(<dict>.items()),按后进先出(LIFO)的顺序遍历键、值或(key, value)对:
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
# Iterating in insertion order (First in, first out)
>>> for item in palette_II.items():
... print(item)
...
('Factory Stone Purple', '#7c677f')
('Green Treeline', '#478559')
('Purple baseline', '#161748')
# Iterating in the reverse direction. (Last in, first out)
>>> for item in reversed(palette_II.items()):
... print (item)
...
('Purple baseline', '#161748')
('Green Treeline', '#478559')
('Factory Stone Purple', '#7c677f')
字典没有内置的排序方法。不过,可以用内置函数sorted()配合dict.items()来对dict的 视图 排序,再用排序后的视图创建一个新字典。与迭代一样,默认按字典的keys排序。
# Default ordering for a dictionary is insertion order (First in, first out).
>>> color_palette = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
# The default sort order for a dictionary uses the keys.
>>> sorted_palette = dict(sorted(color_palette.items()))
>>> sorted_palette
{'Factory Stone Purple': '#7c677f',
'Grassy Green': '#9bc400',
'Green Treeline': '#478559',
'Misty Mountain Pink': '#f9c5bd',
'Purple Mountains Majesty': '#8076a3',
'Purple baseline': '#161748'}
.update()合并字典<dict_one>.update(<dict_two>)可以用来 合并 两个字典。该方法会取出<dict_two>的(key,value)对,并把它们写入<dict_one>:
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple Baseline': '#161748'}
>>> palette_I.update(palette_II)
# Note that new items from palette_II are added.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}
当两个字典中的键 重叠 时,dict_one中的value会被dict_two中对应的value覆盖:
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> palette_I.update(palette_III)
# Overlapping values in palette_I are replaced with values from palette_III
>>> palette_I
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559', 'Purple baseline': '#161748'}
|和|=)运算符合并或更新字典Python 3.9 引入了另一种合并dicts的方式:union运算符。dict_one | dict_two会创建一个 新字典,由dict_one和dict_two的(key, value)对组成。当两个字典有相同的键时,以dict_two的值优先。
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> new_dict = palette_I | palette_II
>>> new_dict
...
{'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
dict_one |= other的行为类似于<dict_one>.update(<other>),但在这里,other既可以是dict,也可以是由(key, value)对组成的可迭代对象:
>>> palette_III = {'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> new_dict |= palette_III
>>> new_dict
...
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
想详细了解字典以及处理字典的方法,官方教程和官方库参考都是很好的起点。
Real Python和Finxter也有非常详尽的 Python 字典文章。
Mecha Munch™ 是一家杂货购物自动化公司,刚刚聘用你来开发他们的点单应用。 你的团队负责构建一个 MVP(最小可行产品),用来管理所有基本的购物车操作,让用户可以添加、删除和排序他们的杂货订单。 好在,所有和金钱、结账相关的功能由另一个团队负责!
MVP 应当允许用户把商品添加到购物车。 可以一次添加一件,也可以一次添加多件。 由于这是 MVP,商品的数量用_重复_来表示。 如果用户想添加 2 个橙子,'Oranges' 会在输入的可迭代对象中出现两次。 如果购物车里已经有这件商品,该商品在购物车中的数量应该增加 1。 如果这件商品是购物车里的_新_商品,则以数量 1 添加。
创建函数 add_item(<current_cart>, <items_to_add>),它接收一个购物车字典,以及任意一个存放待添加商品的、类似列表的可迭代对象作为参数。
它应该返回一个新建的/更新后的用户购物车字典。
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
('Apple', 'Apple', 'Orange', 'Apple', 'Banana'))
{'Banana': 4, 'Apple': 5, 'Orange': 2}
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
['Banana', 'Orange', 'Blueberries', 'Banana'])
{'Banana': 5, 'Apple': 2, 'Orange': 2, 'Blueberries': 1}
哎呀。 看来产品团队正在搞功能蔓延。 他们想给 MVP 增加额外的功能。 现在应用必须通过读取用户笔记应用中的商品来创建购物车。 对用户来说很方便,但对团队来说稍微多了点活儿。
创建函数 read_notes(<notes>),它可以接收任意一个类似列表的可迭代对象作为参数。
该函数应该解析这些商品,并创建一个用户购物车/字典。
每件商品都以数量 1 添加。
然后返回新的用户购物车。
>>> read_notes(('Banana','Apple', 'Orange'))
{'Banana': 1, 'Apple': 1, 'Orange': 1}
>>> read_notes(['Blueberries', 'Pear', 'Orange', 'Banana', 'Apple'])
{'Blueberries' : 1, 'Pear' : 1, 'Orange' : 1, 'Banana' : 1, 'Apple' : 1}
该应用有一个“想法”部分,里面装满了来自各种菜系的成品食谱。 用户可以选择其中任意一个食谱,让它所有的食材自动添加到自己的购物车。 项目经理让你想办法编辑这些“想法”食谱,因为内容团队一直在改动食材和数量。
创建函数 update_recipes(<ideas>, <recipe_updates>),它接收一个“想法”字典和一个食谱更新的可迭代对象作为参数。
该函数应该返回新建的/更新后的“想法”字典。
>>>update_recipes(
{'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
(('Banana Bread', {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),)
)
...
{'Banana Bread': {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3},
'Raspberry Pie': {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}}
>>> update_recipes(
{'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
[('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})]
)
...
{'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2},
'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}
用户一旦开始使用购物车,应用就允许他们按字母顺序对商品排序。 这样更容易找到东西,也能在出现 'potatoes' 和 'Potato' 这类数据录入错误时帮上忙。
创建函数 sort_entries(<cart>),它接收一个购物车/字典作为参数,并返回一个按字母顺序排序的新购物车。
>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
{'Apple': 2, 'Banana':3, 'Orange': 1}
应用需要把指定用户的购物车发送到商店进行履约。 不过,商店里的拣货员需要知道每件商品在哪个货架通道,以及这件商品是否需要冷藏。 所以(相当随意地),“履约购物车”需要按字母倒序排序,并把商品数量与位置、冷藏信息结合起来。
创建函数 send_to_store(<cart>, <aisle_mapping>),它接收一个用户购物车和一个字典,字典中为每件商品记录商店货架通道编号以及是否需要冷藏的 True/False。
该函数应该 return 一个合并后的“履约购物车”,其中为顾客订购的每件商品记录(数量、货架通道和冷藏信息)。
商品应该按_字母倒序_排列。
>>> send_to_store({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2},
{'Banana': ['Aisle 5', False], 'Apple': ['Aisle 4', False], 'Orange': ['Aisle 4', False], 'Milk': ['Aisle 2', True]})
{'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]}
应用不能只是一直不停地接收顾客订单。 最终,商店的各种商品都会断货。 所以你的 MVP 应用需要在用户每次把订单发送到商店时更新商店库存。 否则,顾客会订购实际上已经没有的商品。
创建函数 update_store_inventory(<fulfillment_cart>, <store_inventory>),它接收一个“履约购物车”和一份商店库存。
该函数应该按“履约购物车”中“订购”的数量减少商店库存量,然后返回更新后的商店库存。
当某件商店商品的数量降到 0 时,该数量应该替换为消息 'Out of Stock'。
>>> update_store_inventory({'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]},
{'Banana': [15, 'Aisle 5', False], 'Apple': [12, 'Aisle 4', False], 'Orange': [1, 'Aisle 4', False], 'Milk': [4, 'Aisle 2', True]})
{'Banana': [12, 'Aisle 5', False], 'Apple': [10, 'Aisle 4', False], 'Orange': ['Out of Stock', 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True]}