跳至主要内容

Python 字典推导式:您需要了解的要点

全面了解 Python 字典推导式。学习如何用它创建字典,用它替代(嵌套)for 循环,或结合 map()、filter()、reduce() 的 lambda 函数等!
· 14分钟 阅读

使用 AI 探索

字典(或 Python 中的 dict)是一种存储元素的方式,类似于列表。但与通过索引访问元素不同,您为元素分配一个固定的键,并使用该键来访问元素。您处理的是“键-值”对,这在许多问题上比简单列表更合适。做数据科学时您经常会用到字典,因此掌握字典推导式是一项值得精通的技能。

在本教程中:

  • 首先,您将了解什么是 Python 字典以及如何高效使用它。
  • 接着,您将学习 Python 字典推导式:它是什么、为何重要,以及它如何作为 for 循环和 lambda 函数的替代方案。
  • 您将学习如何在字典推导式中加入条件:包括 if 条件、多个 if 条件,以及 if-else 语句。
  • 最后,您将看到什么是嵌套字典推导式、如何使用它,以及如何用 for 循环重写它。

若想在本教程中轻松运行所有示例代码,您可以免费创建一个 DataLab 工作簿,其中已预装 Python 并包含所有代码示例。若想进一步练习字典推导式,请查看我们的交互式课程 Python 工具箱。

什么是 Python 字典?

Python 中的字典是一种通过特定键而非索引来访问元素的集合。这意味着什么?

想象一下现实世界中的字典。当您想查一个词的意思时,您会用词本身来查,而不是用它可能的索引。Python 字典也是同样的概念:您要查意思的词是键,词义就是值。您无需知道该词在字典中的索引就能找到它的意思。

在 Python 中初始化字典

您可以这样初始化一个字典:

a = {'apple': 'fruit', 'beetroot': 'vegetable', 'cake': 'dessert'}
a['doughnut'] = 'snack'
print(a['apple'])
fruit
print(a[0])
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

<ipython-input-9-00d4a978143a> in <module>()
----> 1 print(a[0])


KeyError: 0

首先,我们创建一个名为 a 的字典,包含三组键值对:'apple': 'fruit'、'beetroot': 'vegetable' 和 'cake': 'dessert'。键是表示项目名称的字符串,值是表示该项目类型或类别的字符串。

接着,我们使用语法 ['doughnut'] = 'snack' 向字典 a 添加一组新的键值对。这会将键 'doughnut' 及其对应的值 'snack' 加入字典。

第三行代码打印字典 a 中与键 'apple' 关联的值。由于 'apple' 是字典中的一个键,代码会打印值 'fruit'。

第四行代码尝试打印字典 a 中与键 0 关联的值,但该键在字典中不存在。这会导致 KeyError,当尝试访问字典中不存在的键时便会抛出此错误。

Python 字典中的数据类型

字典中的项目可以是任何数据类型。请看下面更多示例以加深理解:

创建一个包含四组键值对的字典 a:

a = {'one': 1, 'two': 'to', 'three': 3.0, 'four': [4,4.0]}
print(a)

将字典 a 中与键 'one' 关联的值更新为 1.0。

# Update a dictionary
a['one'] = 1.0 
print(a)
{'four': [4, 4.0], 'two': 'to', 'three': 3.0, 'one': 1.0}

从字典 a 中删除与键 'one' 关联的键值对。

# Delete a single element
del a['one'] 
print(a)
{'four': [4, 4.0], 'two': 'to', 'three': 3.0}

使用 clear() 方法移除字典 a 中的所有键值对。

# Delete all elements in the dictionary
a.clear()
print(a)
{}

使用 del 关键字删除字典 a。

# Delete the dictionary
del a 
print(a)
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-12-701c9d6596da> in <module>()
      1 del a #Deletes the dictionary
----> 2 print(a)


NameError: name 'a' is not defined

需要记住的一点是,字典中的键必须唯一;不允许重复。不过,若出现重复键,Python 并不会报错,而是会以最后一次出现的该键为准,忽略第一次的键值对。自行验证如下:

sweet_dict = {'a1': 'cake', 'a2':'cookie', 'a1': 'icecream'}
print(sweet_dict['a1'])
icecream

Python 字典推导式

字典推导式是一种将一个字典转换为另一个字典的方法。在转换过程中,原字典中的条目可以按条件选择性地包含到新字典中,同时每个条目也可以按需进行转换。

好的推导式可以让代码更具表达力,从而更易阅读。关键在于不要让推导式复杂到必须费力破解其含义,务必坚持“易读”的原则。

在 Python 中实现字典推导式的方式,是能够访问字典的 key 对象和 value 对象。

如何做到?

Python 已为您准备好内置方法:

dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# Put all keys of `dict1` in a list and returns the list
dict1.keys()
dict_keys(['c', 'd', 'a', 'b'])
# Put all values saved in `dict1` in a list and returns the list
dict1.values()
dict_values([3, 4, 1, 2])

上述代码创建了一个包含四组键值对的 Python 字典 dict1。调用 keys() 方法会返回一个包含字典所有键的视图对象。

同样地,调用 values() 方法会返回一个包含字典所有值的视图对象。

这两个视图对象的行为类似集合,可分别用于对键和值执行集合操作。

请注意,这些视图对象中的键和值顺序并不保证与它们被加入字典时的顺序一致。

基础的值变换 

先从一个简单的字典推导式开始:

dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
# Double each value in the dictionary
double_dict1 = {k:v*2 for (k,v) in dict1.items()}
print(double_dict1)
{'e': 10, 'a': 2, 'c': 6, 'b': 4, 'd': 8}

在上述推导式中,我们从字典 dict1 创建了一个新字典 double_dict1,方法是将每个值都乘以 2。

您也可以对键做变换。例如,创建与上面相同的字典,同时更改键名:

dict1_keys = {k*2:v for (k,v) in dict1.items()}
print(dict1_keys)
{'dd': 4, 'ee': 5, 'aa': 1, 'bb': 2, 'cc': 3}

使用 items() 方法

既然您已经知道如何访问字典中的所有键及其值,也可以通过 items() 方法访问字典中的每一组键值对:

dict1.items()
dict_items([('c', 3), ('d', 4), ('a', 1), ('b', 2)])

以下是您在 Python 中进行字典推导式时可遵循的一般模板:

dict_variable = {key:value for (key,value) in dictonary.items()}

这是最基本、最简单的模板。随着加入条件,这个结构会逐步变得复杂。

使用 fromkeys() 方法

Python 还提供了 fromkeys() 方法,可用统一的值为指定的一组键创建字典。该方法在用默认值初始化字典时非常有用。来看示例:

# Create a dictionary with keys ranging from 0 to 4, all set to True
my_dict = dict.fromkeys(range(5), True)
print(my_dict)
{0: True, 1: True, 2: True, 3: True, 4: True}

在本例中,fromkeys() 创建了一个字典,键为 0 到 4 的数字,每个键都被赋值为 True。这种方式能简化诸如为后续更新或处理而初始化字典的任务。

为何使用字典推导式?

字典推导式是一个强大的概念,可用来替代 for 循环和 lambda 函数。不过,并非所有 for 循环都能写成字典推导式,但所有字典推导式都能用 for 循环来表达。

考虑以下问题:您想创建一个新字典,其键为 0-10 范围内能被 2 整除的数,值为该数的平方。

我们分别用 for 循环和字典推导式来解决:

numbers = range(10)
new_dict_for = {}

# Add values to `new_dict` using for loop
for n in numbers:
    if n%2==0:
        new_dict_for[n] = n**2

print(new_dict_for)
{0: 0, 8: 64, 2: 4, 4: 16, 6: 36}
# Use dictionary comprehension
new_dict_comp = {n:n**2 for n in numbers if n%2 == 0}

print(new_dict_comp)
{0: 0, 8: 64, 2: 4, 4: 16, 6: 36}

替代 for 循环

for 循环用于在程序中重复执行某个操作或指令块若干次。然而,嵌套 for 循环(一个 for 循环套在另一个 for 循环中)可能会变得混乱且复杂。在这种情况下,字典推导式更胜一筹,能够简化代码的可读性和理解程度。

提示:想了解更多 Python 循环内容,请查看 DataCamp 的 Python 中的循环 教程。

替代 lambda 函数

Lambda 函数是一种创建小型匿名函数的方式。它们是没有名称的函数,仅在创建之处短暂使用。Lambda 函数主要与 filter()、map() 和 reduce() 函数组合使用。

来看一个配合 map() 使用的 lambda 示例:

# Initialize `fahrenheit` dictionary 
fahrenheit = {'t1':-30, 't2':-20, 't3':-10, 't4':0}

#Get the corresponding `celsius` values
celsius = list(map(lambda x: (float(5)/9)*(x-32), fahrenheit.values()))

#Create the `celsius` dictionary
celsius_dict = dict(zip(fahrenheit.keys(), celsius))

print(celsius_dict)
{'t2': -28.88888888888889, 't3': -23.333333333333336, 't1': -34.44444444444444, 't4': -17.77777777777778}

一个更详尽的 Python 字典推导式示例

再看一个情境:您想把一个华氏温度字典转换为摄氏温度字典。

我们分解下代码:首先,您需要定义一个将华氏转换为摄氏的数学公式。

在代码中,这是借助 lambda 函数完成的。然后将该函数作为参数传给 map(),它会将操作应用于 fahrenheit.values() 列表中的每个元素。

还记得 values() 函数吗?它返回一个包含字典存储条目的列表。

此时您得到的是包含摄氏温度值的列表,但我们需要的结果是一个字典。

Python 提供了内置函数 zip(),可遍历迭代器的元素并将其配对聚合。您可以在此 Python 示例中了解更多关于 zip() 的信息。

在上面的示例中,zip 将 fahrenheit.keys() 与 celsius 列表中的元素配对,形成可用 dict 函数组装为字典的键值对,即所需结果。

现在,尝试用字典推导式来解决同一问题:

# Initialize the `fahrenheit` dictionary 
fahrenheit = {'t1': -30,'t2': -20,'t3': -10,'t4': 0}

# Get the corresponding `celsius` values and create the new dictionary
celsius = {k:(float(5)/9)*(v-32) for (k,v) in fahrenheit.items()}

print(celsius_dict)
{'t2': -28.88888888888889, 't3': -23.333333333333336, 't1': -34.44444444444444, 't4': -17.77777777777778}

下面是上述代码的要点分解: 

  1. 创建一个名为 fahrenheit 的字典,包含四组以华氏表示的温度读数。

  2. 定义一个新的字典 celsius。

  3. 使用字典推导式遍历 fahrenheit,并用公式 (5/9) * (F-32) 将华氏温度转换为摄氏温度。

  4. 对于 fahrenheit 中的每个键值对,将键赋给变量 k,将值赋给变量 v。

  5. 用上述公式计算对应的摄氏温度值,并以键值对的形式加入 celsius 字典。

  6. 将所得的 celsius 字典打印到控制台。

如您所见,相比第一种实现需要两步并理解三个函数(lambda、map() 和 zip())的工作方式,使用字典推导式仅用一行代码就能完成。

此外,使用字典推导式的解决方案更直观、易理解。因此,字典推导式可以作为 lambda 函数的良好替代。

在字典推导式中加入条件

解决问题时您经常需要加入条件。下面来看看如何在字典推导式中加入条件,使其更强大。

If 条件

假设您需要从给定字典中创建一个新字典,但只保留值大于 2 的项目。这意味着要在前面看到的模板中加入一个条件……

dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

# Check for items greater than 2
dict1_cond = {k:v for (k,v) in dict1.items() if v>2}

print(dict1_cond)
{'e': 5, 'c': 3, 'd': 4}

这并不难!但如果您有多个条件呢?

多个 if 条件

在上面的题目中,如果不仅要筛出值大于 2 的项目,还要同时检查它们是否为 2 的倍数,该怎么办?

dict1_doubleCond = {k:v for (k,v) in dict1.items() if v>2 if v%2 == 0}
print(dict1_doubleCond)
{'d': 4}

要加入多个条件,您只需在推导式中依次添加条件即可。但要谨慎明确您的目标。请记住,连续的 if 语句等同于在它们之间加上 and。

再看一个包含三个条件的示例:

dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f':6}

dict1_tripleCond = {k:v for (k,v) in dict1.items() if v>2 if v%2 == 0 if v%3 == 0}

print(dict1_tripleCond)
{'f': 6}

对应的 for 循环写法如下:

dict1_tripleCond = {}

for (k,v) in dict1.items():
    if (v>=2 and v%2 == 0 and v%3 == 0):
            dict1_tripleCond[k] = v

print(dict1_tripleCond)
{'f': 6}

If-else 条件

在字典推导式中处理 if-else 条件也很简单。请看下面的示例:

dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f':6}

# Identify odd and even entries
dict1_tripleCond = {k:('even' if v%2==0 else 'odd') for (k,v) in dict1.items()}

print(dict1_tripleCond)
{'f': 'even', 'c': 'odd', 'b': 'even', 'd': 'even', 'e': 'odd', 'a': 'odd'}

嵌套字典推导式

嵌套是一个将数据按层组织或让对象包含其他相似对象的编程概念。您一定常见到嵌套的 if 结构,即 if 条件中再套一个 if 条件。

类似地,字典可以嵌套,因此字典推导式也可以嵌套。来看这意味着什么:

nested_dict = {'first':{'a':1}, 'second':{'b':2}}
float_dict = {outer_k: {float(inner_v) for (inner_k, inner_v) in outer_v.items()} for (outer_k, outer_v) in nested_dict.items()}
print(float_dict)
{'first': {1.0}, 'second': {2.0}}

这是一个嵌套字典的示例。nested_dict 的键为 first 与 second,其对应的值是字典对象。代码对内层字典的值进行处理,将其转换为浮点数,然后将外层键与新的浮点内层值组合成一个新字典。

这段代码也使用了嵌套字典推导式,即一个字典推导式里面再嵌套另一个。嵌套后推导式也可能变得难以阅读和理解,反而背离了使用推导式“提升可读性”的初衷。

随着您处理的字典结构变复杂,相应的推导式也会变复杂。在这种情况下,避免使用过于复杂的推导式可能更好。

注意,您也可以用嵌套 for 循环重写上面的代码段:

nested_dict = {'first':{'a':1}, 'second':{'b':2}}

for (outer_k, outer_v) in nested_dict.items():
    for (inner_k, inner_v) in outer_v.items():
        outer_v.update({inner_k: float(inner_v)})
nested_dict.update({outer_k:outer_v})

print(nested_dict)
{'first': {'a': 1.0}, 'second': {'b': 2.0}}

结语

希望现在您已经了解了 Python 中的字典、推导式的概念,以及字典推导式为何有用、在何处有用。 

多练习,Python 才会更熟!查看我们的部分精选 Python 技巧与窍门 来改进您的代码,或参加 Python 数据基础学习路径掌握核心要点。 

Python 字典推导式常见问答

什么是 Python 中的字典推导式?

字典推导式是在 Python 中创建字典的一种简洁且可读的方式。它提供了一种从列表、元组或集合等可迭代对象生成新字典的方法。

Python 中字典推导式的语法是什么?

Python 中字典推导式的语法如下:{key:value for (key,value) in iterable}

如何用字典推导式从列表创建字典?

您可以通过遍历列表,并将列表元素设为新字典中的键和值,使用字典推导式从列表创建字典。

在 Python 的字典推导式中可以使用条件语句吗?

可以。您可以在 Python 的字典推导式中使用条件语句来过滤掉某些元素。

使用字典推导式有哪些好处?

使用字典推导式的好处在于其简洁、可读,并能减少创建字典所需的代码量。它还允许在一行代码中完成字典元素的过滤与变换。

Sejal Jaiswal

作者

Sejal Jaiswal

我曾在多个行业工作,身兼数职:软件开发工程师、机器学习研究员、数据科学家、产品经理。但归根结底,我是一名热爱学习并乐于分享知识的程序员!

主题