轨道
/
Python
Python
/
练习
/
机甲零食管理
机甲零食管理

机甲零食管理

学习练习

简介

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(最小可行产品),用来管理所有基本的购物车操作,让用户可以添加、删除和排序他们的杂货订单。 好在,所有和金钱、结账相关的功能由另一个团队负责!

1. 将商品添加到用户的购物车

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}

2. 读取用户笔记应用中列出的商品

哎呀。 看来产品团队正在搞功能蔓延。 他们想给 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}

3. 更新食谱“想法”部分

该应用有一个“想法”部分,里面装满了来自各种菜系的成品食谱。 用户可以选择其中任意一个食谱,让它所有的食材自动添加到自己的购物车。 项目经理让你想办法编辑这些“想法”食谱,因为内容团队一直在改动食材和数量。

创建函数 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}}

4. 对用户购物车中的商品排序

用户一旦开始使用购物车,应用就允许他们按字母顺序对商品排序。 这样更容易找到东西,也能在出现 'potatoes' 和 'Potato' 这类数据录入错误时帮上忙。

创建函数 sort_entries(<cart>),它接收一个购物车/字典作为参数,并返回一个按字母顺序排序的新购物车。

>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
{'Apple': 2, 'Banana':3, 'Orange': 1}

5. 将用户购物车发送到商店进行履约

应用需要把指定用户的购物车发送到商店进行履约。 不过,商店里的拣货员需要知道每件商品在哪个货架通道,以及这件商品是否需要冷藏。 所以(相当随意地),“履约购物车”需要按字母倒序排序,并把商品数量与位置、冷藏信息结合起来。

创建函数 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]}

6. 更新商店库存,反映用户所订商品

应用不能只是一直不停地接收顾客订单。 最终,商店的各种商品都会断货。 所以你的 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]}
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Python Exercism

准备好开始 机甲零食管理 了吗?

注册 Exercism,借助 17 个概念146 个练习 和真人导师指导,学习并掌握 Python,全部免费。