嵌套

有时你会想要将多个字典存储在一个列表中,或者一个项目列表作为字典中的一个值。 这称为嵌套。 您可以将字典嵌套在列表中,将项目列表嵌套在字典中,甚至可以将字典嵌套在另一个字典中。 嵌套是一个强大的功能,如以下示例所示。

字典列表

alien_0 字典包含有关一个外星人的各种信息,但它没有空间存储有关第二个外星人的信息,更不用说满屏的外星人了。 你如何管理一支外星舰队? 一种方法是制作一个外星人列表,其中每个外星人都是关于该外星人的信息字典。 例如,以下代码构建了一个包含三个外星人的列表:

alien_0 = {'color': 'green', 'points': 5}
alien_1 = {'color': 'yellow', 'points': 10}
alien_2 = {'color': 'red', 'points': 15}

aliens = [alien_0, alien_1, alien_2] (1)

for alien in aliens:
  print(alien)
1 我们首先创建三个字典,每个字典代表一个不同的外星人。 我们将这些字典中的每一个都存储在一个名为 aliens 的列表中。 最后,我们遍历列表并打印出每个外星人:
{'color': 'green', 'points': 5}
{'color': 'yellow', 'points': 10}
{'color': 'red', 'points': 15}

一个更现实的例子将涉及三个以上的外星人,每个外星人都有自动生成的代码。 在下面的示例中,我们使用 range() 创建了一个由 30 个外星人组成的舰队:

# Make an empty list for storing aliens.
aliens = []

# Make 30 green aliens.
for alien_number in range(30): (1)
    new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'} (2)
    aliens.append(new_alien) (3)

for alien in aliens[:3]:
    if alien['color'] == 'green':
        alien['color'] = 'yellow'
        alien['speed'] = 'medium'
        alien['points'] = 10

# Show the first 5 aliens.
for alien in aliens[:5]: (4)
    print(alien)
print("...")

# Show how many aliens have been created.
print(f"Total number of aliens: {len(aliens)}")
1 此示例以一个空列表开始,用于保存将要创建的所有外星人。 range() 函数返回一系列数字,它只是告诉 Python 我们希望循环重复多少次。
2 每次循环运行时,我们都会创建一个新的外星人,
3 然后将每个新的外星人附加到列表外星人。
4 我们使用切片来打印前五个外星人,

最后,我们打印列表的长度来证明我们实际上已经生成了 30 个外星人的完整舰队:

{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
...
Total number of aliens: 30

这些外星人都有相同的特征,但 Python 将每一个都视为一个单独的对象,这使我们可以单独修改每个外星人。

你怎么能和这样一群外星人一起工作呢? 想象一下,游戏的一个方面是一些外星人随着游戏的进行而改变颜色并移动得更快。 当需要改变颜色时,我们可以使用 for 循环和 if 语句来改变外星人的颜色。 例如,要将前三个外星人更改为黄色、中速外星人,每个价值 10 分,我们可以这样做:

# Make an empty list for storing aliens.
aliens = []

# Make 30 green aliens.
for alien_number in range(30):
    new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}
    aliens.append(new_alien)

for alien in aliens[:3]:
    if alien['color'] == 'green':
        alien['color'] = 'yellow'
        alien['speed'] = 'medium'
        alien['points'] = 10

# Show the first 5 aliens.
for alien in aliens[:5]:
    print(alien)
print("...")

# Show how many aliens have been created.
print(f"Total number of aliens: {len(aliens)}")

因为我们要修改前三个外星人,所以我们循环遍历仅包含前三个外星人的切片。 现在所有的外星人都是绿色的,但情况并非总是如此,所以我们写了一个 if 语句来确保我们只修改绿色外星人。 如果外星人是绿色的,我们将颜色更改为“黄色”,将速度更改为“中等”,并将点值更改为 10,如以下输出所示:

{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
...

您可以通过添加一个 elif 块来扩展此循环,该块将黄色外星人变成红色、快速移动的外星人,每个价值 15 分。 如果不再次显示整个程序,该循环将如下所示:

for alien in aliens[0:3]:
    if alien['color'] == 'green':
        alien['color'] = 'yellow'
        alien['speed'] = 'medium'
        alien['points'] = 10
    elif alien['color'] == 'yellow':
        alien['color'] = 'red'
        alien['speed'] = 'fast'
        alien['points'] = 15

当每个字典包含关于一个对象的多种信息时,通常将多个字典存储在一个列表中。 例如,您可以为网站上的每个用户创建一个字典,就像我们在第 99 页的 user.py 中所做的那样,并将各个字典存储在一个名为 users 的列表中。 列表中的所有字典都应具有相同的结构,因此您可以遍历列表并以相同的方式处理每个字典对象。

在字典中存储列表

与其将字典放入列表中,有时将列表放入字典中更有用。 例如,考虑如何描述某人订购的披萨。 如果您只使用一个列表,那么您真正可以存储的就是比萨饼配料的列表。 使用字典,配料列表可以只是您所描述的比萨饼的一个方面。

在下面的示例中,为每个比萨存储了两种信息:一种外皮类型和一个配料列表。 浇头列表是与键“浇头”关联的值。 为了使用列表中的项目,我们给出字典的名称和键 'toppings',就像字典中的任何值一样。 我们没有返回单个值,而是得到了一个浇头列表:

# Store information about a pizza being ordered.
pizza = {
    'crust': 'thick',
    'toppings': ['mushrooms', 'extra cheese'],
    }

# Summarize the order.
print(f"You ordered a {pizza['crust']}-crust pizza " (1)
    "with the following toppings:")

for topping in pizza['toppings']: (2)
    print(f"\t{topping}")
1 我们从一个字典开始,该字典包含有关已订购的比萨饼的信息。 字典中的一个关键字是“crust”,关联的值是字符串“thick”。 下一个键“toppings”有一个列表作为其值,用于存储所有请求的浇头。 我们在构建比萨之前打印订单。 当您需要在 print() 调用中打断一长行时,请选择一个适当的点来打断正在打印的行,并以引号结束该行。 缩进下一行,添加左引号,然后继续字符串。
2 Python 会自动组合它在括号内找到的所有字符串。 为了打印浇头,我们编写了一个 for 循环。 要访问浇头列表,我们使用键“toppings”,Python 从字典中获取浇头列表。

以下输出总结了我们计划制作的披萨:

You ordered a thick-crust pizza with the following toppings:
  mushrooms
  extra cheese

您可以在任何时候将多个值与字典中的单个键相关联时,将列表嵌套在字典中。 在前面最喜欢的编程语言的例子中,如果我们将每个人的回答存储在一个列表中,人们可以选择不止一种最喜欢的语言。 当我们遍历字典时,与每个人关联的值将是一个语言列表,而不是单一语言。 在字典的 for 循环中,我们使用另一个 for 循环来遍历与每个人相关的语言列表:

favorite_languages = {
      'jen': ['python', 'rust'],
      'sarah': ['c'],
      'edward': ['rust', 'go'],
      'phil': ['python', 'haskell'],
      }

for name, languages in favorite_languages.items():
    print(f"\n{name.title()}'s favorite languages are:") (1)
    for language in languages: (2)
        print(f"\t{language.title()}")
1 favorite_languages 中与每个名称关联的值现在是一个列表。 请注意,有些人最喜欢一种语言,而其他人则有多种最喜欢的语言。 当我们遍历字典时,我们使用变量名语言来保存字典中的每个值,因为我们知道每个值都是一个列表。
2 在主词典循环中,我们使用另一个 for 循环来遍历每个人最喜欢的语言列表。 现在每个人都可以列出尽可能多的喜欢的语言:
Jen's favorite languages are:
    Python
    Rust

Sarah's favorite languages are:
  C

Edward's favorite languages are:
    Rust
    Go

Phil's favorite languages are:
    Python
    Haskell

要进一步优化此程序,您可以在字典的 for 循环开头包含一个 if 语句,通过检查 len(languages) 的值来查看每个人是否有不止一种最喜欢的语言。 如果一个人有多个最爱,输出将保持不变。 如果此人只有一种最喜欢的语言,您可以更改措辞以反映这一点。 例如,您可以说,“Sarah 最喜欢的语言是 C。”

您不应该将列表和字典嵌套得太深。 如果您嵌套的项目比您在前面的示例中看到的要深得多,或者如果您正在使用具有大量嵌套的其他人的代码,那么很可能有更简单的方法来解决问题。

在字典中存储字典

您可以将一个字典嵌套在另一个字典中,但是这样做时您的代码会很快变得复杂。 例如,如果您的网站有多个用户,每个用户都有唯一的用户名,则可以将用户名用作字典中的键。 然后,您可以通过使用字典作为与用户名关联的值来存储有关每个用户的信息。 在下面的清单中,我们存储了关于每个用户的三部分信息:他们的名字、姓氏和位置。 我们将通过遍历用户名和与每个用户名关联的信息字典来访问此信息:

users = {
    'aeinstein': {
        'first': 'albert',
        'last': 'einstein',
        'location': 'princeton',
        },

    'mcurie': {
        'first': 'marie',
        'last': 'curie',
        'location': 'paris',
        },

    }

for username, user_info in users.items(): (1)
    print(f"\nUsername: {username}") (2)
    full_name = f"{user_info['first']} {user_info['last']}" (3)
    location = user_info['location']
    
    print(f"\tFull name: {full_name.title()}") (4)
    print(f"\tLocation: {location.title()}")
1 我们首先定义一个名为 users 的字典,它有两个键:用户名“aeinstein”和“mcurie”各一个。 与每个键关联的值是一个字典,其中包括每个用户的名字、姓氏和位置。 然后,我们遍历用户字典。 Python 将每个键分配给变量 username,并将与每个用户名关联的字典分配给变量 user_info。
2 一旦进入主字典循环,我们打印用户名。
3 然后,我们开始访问内部字典。 包含用户信息字典的变量 user_info 具有三个键:'first'、'last' 和 'location'。
4 我们使用每个键为每个人生成格式整齐的全名和位置,然后打印我们对每个用户了解的摘要:
Username: aeinstein
    Full name: Albert Einstein
    Location: Princeton

Username: mcurie
    Full name: Marie Curie
    Location: Paris

请注意,每个用户的字典结构都是相同的。 虽然 Python 不需要,但这种结构使嵌套字典更易于使用。 如果每个用户的字典有不同的键,for 循环中的代码会更复杂。

自己试试
6-7.People

从你为练习 6-1(第 98 页)编写的程序开始。 制作两个代表不同人的新词典,并将所有三个词典存储在一个名为 people 的列表中。 循环浏览您的人员列表。 当你遍历列表时,打印出你对每个人的了解。

6-8.Pets

制作几本词典,每本词典代表不同的宠物。 在每本词典中,包括动物的种类和主人的名字。 将这些字典存储在一个名为 pets 的列表中。 接下来,循环遍历您的列表,并打印出您对每只宠物的了解。

6-9.Favorite Places

制作一本名为 favorite_places 的字典。 想出三个名字用作字典中的键,并为每个人存储一到三个最喜欢的地方。 为了让这个练习更有趣一点,请一些朋友说出他们最喜欢的几个地方。 循环字典,打印每个人的名字和他们最喜欢的地方。

6-10.Favorite Numbers

根据练习 6-2(第 98 页)修改你的程序,这样每个人都可以拥有多个最喜欢的号码。 然后打印每个人的名字和他们最喜欢的号码。

6-11.Cities

制作一本名为城市的字典。 使用三个城市的名称作为字典中的关键字。 创建一个关于每个城市的信息字典,包括该城市所在的国家、其大概人口以及关于该城市的一个事实。 每个城市字典的键应该是国家、人口和事实。 打印每个城市的名称以及您存储的有关它的所有信息。

6-12.Extensions

我们现在正在处理足够复杂的示例,可以通过多种方式对其进行扩展。 使用本章中的示例程序之一,并通过添加新的键和值、更改程序的上下文或改进输出的格式来扩展它。

总结

在本章中,您学习了如何定义字典以及如何使用存储在字典中的信息。 您学习了如何访问和修改字典中的单个元素,以及如何遍历字典中的所有信息。 您学习了遍历字典的键值对、键和值。 您还学习了如何在列表中嵌套多个字典、在字典中嵌套列表以及在字典中嵌套字典。

在下一章中,您将了解 while 循环以及如何接受程序用户的输入。 这将是一个激动人心的章节,因为您将学习如何让您的所有程序都具有交互性:它们将能够响应用户输入。