MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python字典与集合推导式

2023-03-037.9k 阅读

Python字典推导式

字典推导式基础语法

在Python中,字典推导式是一种简洁且强大的创建字典的方式。它允许我们通过一种紧凑的表达式,从可迭代对象(如列表、元组、集合等)快速生成字典。其基本语法为:

{key_expression: value_expression for item in iterable}

这里的key_expression是用于生成字典键的表达式,value_expression是用于生成字典值的表达式,item是从iterable中依次取出的元素。例如,我们有一个包含数字的列表,想要创建一个字典,其中键是列表中的数字,值是该数字的平方。可以这样写:

nums = [1, 2, 3, 4, 5]
square_dict = {num: num ** 2 for num in nums}
print(square_dict)

上述代码执行后,square_dict将是{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

带条件的字典推导式

字典推导式还支持添加条件。语法如下:

{key_expression: value_expression for item in iterable if condition}

条件condition是一个布尔表达式,只有当conditionTrue时,对应的键值对才会被添加到新生成的字典中。比如,我们只想获取列表中偶数的平方作为字典的键值对:

nums = [1, 2, 3, 4, 5]
even_square_dict = {num: num ** 2 for num in nums if num % 2 == 0}
print(even_square_dict)

上述代码会输出{2: 4, 4: 16},因为只有2和4是偶数,满足条件num % 2 == 0

嵌套字典推导式

在处理复杂数据结构时,可能会用到嵌套的字典推导式。例如,有一个包含多个列表的列表,每个内部列表包含两个元素,我们想将其转换为一个字典,外部字典的键是第一个元素,值是一个字典,内部字典的键是第二个元素,值为1。如下代码展示了这种情况:

nested_list = [['a', 1], ['b', 2], ['a', 3], ['b', 4]]
nested_dict = {outer[0]: {outer[1]: 1} for outer in nested_list}
print(nested_dict)

上述代码输出{'a': {1: 1, 3: 1}, 'b': {2: 1, 4: 1}}。在这个例子中,外层的for循环遍历nested_list,每次取一个子列表outer,然后通过字典推导式构建内部字典,最终形成嵌套字典。

字典推导式与传统循环创建字典的性能对比

虽然字典推导式在代码简洁性上具有很大优势,但了解它与传统循环创建字典在性能上的差异也是很有必要的。

  1. 传统循环创建字典示例
nums = list(range(10000))
result_dict_loop = {}
for num in nums:
    result_dict_loop[num] = num ** 2
  1. 字典推导式创建字典示例
nums = list(range(10000))
result_dict_comprehension = {num: num ** 2 for num in nums}

通过使用timeit模块来进行性能测试,代码如下:

import timeit

nums = list(range(10000))

def create_dict_loop():
    result = {}
    for num in nums:
        result[num] = num ** 2
    return result

def create_dict_comprehension():
    return {num: num ** 2 for num in nums}

loop_time = timeit.timeit(create_dict_loop, number = 1000)
comprehension_time = timeit.timeit(create_dict_comprehension, number = 1000)

print(f"传统循环创建字典1000次耗时: {loop_time} 秒")
print(f"字典推导式创建字典1000次耗时: {comprehension_time} 秒")

通常情况下,字典推导式的性能会优于传统循环。这是因为字典推导式是在底层用C语言实现的,而传统循环是Python字节码,在执行时,C语言实现的部分效率更高。但在一些非常简单的场景下,由于字典推导式本身也有一定的创建开销,性能差异可能不明显。

字典推导式在实际项目中的应用场景

  1. 数据清洗与转换:在处理数据时,经常需要将一种数据格式转换为字典格式。例如,从CSV文件读取的数据可能是一个包含行数据的列表,每行又是一个包含字段值的列表。可以使用字典推导式将其转换为更方便处理的字典结构,键为字段名,值为对应的值。
  2. 配置文件处理:配置文件通常以键值对的形式存储信息。在Python中,可以使用字典推导式从配置文件的内容(如ini文件解析后的内容)快速构建一个字典,方便在程序中使用配置信息。
  3. 数据聚合与统计:假设我们有一个包含学生成绩的列表,每个元素是一个包含学生姓名和成绩的元组。可以使用字典推导式来统计每个学生的平均成绩,键为学生姓名,值为平均成绩。例如:
student_scores = [('Alice', 85), ('Bob', 90), ('Alice', 95), ('Bob', 88)]
average_scores = {name: sum([score for n, score in student_scores if n == name]) / len([score for n, score in student_scores if n == name]) for name in set([name for name, score in student_scores])}
print(average_scores)

上述代码先通过set获取所有学生的名字,然后通过字典推导式计算每个学生的平均成绩。

Python集合推导式

集合推导式基础语法

集合推导式用于快速创建集合。集合是一种无序且不包含重复元素的数据结构。集合推导式的基本语法与字典推导式和列表推导式有相似之处:

{expression for item in iterable}

这里的expression是用于生成集合元素的表达式,item是从iterable中依次取出的元素。例如,我们有一个包含重复数字的列表,想要创建一个集合,去除重复元素,并对每个元素进行平方运算。可以这样写:

nums = [1, 2, 2, 3, 4, 4, 5]
square_set = {num ** 2 for num in nums}
print(square_set)

上述代码执行后,square_set可能是{1, 4, 9, 16, 25}(由于集合是无序的,元素顺序可能不同)。因为集合会自动去除重复元素,所以即使原列表中有重复数字,生成的集合中也只会包含唯一的平方值。

带条件的集合推导式

与字典推导式类似,集合推导式也支持添加条件。语法为:

{expression for item in iterable if condition}

例如,我们只想获取列表中偶数的平方作为集合的元素:

nums = [1, 2, 3, 4, 5]
even_square_set = {num ** 2 for num in nums if num % 2 == 0}
print(even_square_set)

上述代码会输出{4, 16},因为只有2和4是偶数,其平方值会被添加到集合中。

集合推导式与列表推导式的区别

  1. 元素唯一性:列表推导式生成的列表可以包含重复元素,而集合推导式生成的集合会自动去除重复元素。例如:
nums = [1, 2, 2, 3, 3, 3]
list_comprehension = [num ** 2 for num in nums]
set_comprehension = {num ** 2 for num in nums}
print(list_comprehension)
print(set_comprehension)

输出结果中,list_comprehension[1, 4, 4, 9, 9, 9],而set_comprehension{1, 4, 9}。 2. 数据结构特性:列表是有序的数据结构,通过索引访问元素;而集合是无序的,不能通过索引访问元素,主要用于成员检测(如in操作)和去重。这决定了在不同的应用场景下选择使用列表推导式还是集合推导式。如果需要保持元素顺序且允许重复元素,应使用列表推导式;如果只关心元素的唯一性和快速成员检测,集合推导式更为合适。

嵌套集合推导式

嵌套集合推导式相对较少使用,但在处理复杂数据结构时可能会用到。例如,有一个包含多个列表的列表,每个内部列表包含一些数字,我们想创建一个集合,包含所有内部列表中大于5的数字的平方。代码如下:

nested_list = [[1, 6, 3], [7, 4, 8], [2, 9, 5]]
result_set = {num ** 2 for sublist in nested_list for num in sublist if num > 5}
print(result_set)

上述代码中,外层for循环遍历nested_list中的每个子列表,内层for循环遍历子列表中的每个数字,当数字大于5时,将其平方添加到集合中。最终输出的集合可能是{36, 49, 64, 81}(顺序不定)。

集合推导式的性能优化与注意事项

  1. 性能优化:集合推导式在创建集合时效率较高,尤其是在处理大量数据时。与使用传统循环逐个添加元素到集合相比,集合推导式利用了底层的优化机制。例如,在添加大量元素时,使用集合推导式:
import timeit

nums = list(range(10000))

def create_set_loop():
    result = set()
    for num in nums:
        result.add(num ** 2)
    return result

def create_set_comprehension():
    return {num ** 2 for num in nums}

loop_time = timeit.timeit(create_set_loop, number = 1000)
comprehension_time = timeit.timeit(create_set_comprehension, number = 1000)

print(f"传统循环创建集合1000次耗时: {loop_time} 秒")
print(f"集合推导式创建集合1000次耗时: {comprehension_time} 秒")

通常集合推导式的耗时会更少,因为其底层实现更为高效。 2. 注意事项:由于集合是无序的,在使用集合推导式时,不要依赖元素的顺序。如果需要保持元素顺序,应考虑使用其他数据结构,如列表。另外,在集合推导式中进行复杂计算时,要注意性能问题。如果计算过于复杂,可以先将相关计算逻辑提取到函数中,再在集合推导式中调用函数,这样可以提高代码的可读性和可维护性。例如:

def complex_calculation(num):
    # 假设这里有复杂的计算逻辑
    return num * 2 + 1

nums = [1, 2, 3, 4, 5]
result_set = {complex_calculation(num) for num in nums}
print(result_set)

集合推导式在实际项目中的应用场景

  1. 数据去重与筛选:在数据处理过程中,经常需要从大量数据中去除重复项并筛选出符合特定条件的数据。例如,从日志文件中读取的IP地址列表可能包含重复的IP,使用集合推导式可以快速去除重复IP,并筛选出特定网段的IP。
  2. 数学集合运算:在一些涉及数学集合运算的场景中,集合推导式非常有用。例如,有两个列表分别表示两个集合的元素,我们可以使用集合推导式快速找到两个集合的交集、并集、差集等。假设我们有两个列表list1list2
list1 = [1, 2, 3, 4, 5]
list2 = [3, 4, 5, 6, 7]
intersection = {num for num in list1 if num in list2}
union = {num for num in list1 + list2}
difference = {num for num in list1 if num not in list2}
print("交集:", intersection)
print("并集:", union)
print("差集:", difference)

上述代码通过集合推导式实现了简单的集合运算。 3. 快速成员检测:在程序中需要频繁检测某个元素是否在集合中时,可以使用集合推导式快速创建集合,然后使用in操作进行高效的成员检测。例如,在游戏开发中,需要检测玩家是否在某个已注册玩家的集合中,使用集合推导式创建集合并进行检测可以提高程序的运行效率。

总之,字典推导式和集合推导式是Python中非常实用的工具,它们不仅可以使代码更加简洁,还能在一定程度上提高程序的性能。在实际编程中,根据具体的需求和场景合理运用这两种推导式,可以让我们的代码更加优雅和高效。