【0016】Python数据类型-不可变集合详解
如果你觉得我的文章写的不错,请关注我哟,请点赞、评论,收藏此文章,谢谢!
本文内容体系结构如下:
在Python中,除了我们常见的可变集合(Set)外,还有一种不可变集合(Frozenset)。不可变集合是一种哈希不可变的集合类型,这意味着一旦创建,其元素就不能被添加、删除或修改。本文将详细介绍Python中的不可变集合,适合零基础小白学习。
一、什么是不可变集合(Frozenset)
1.1 不可变集合概述
在Python中,不可变集合(immutable set)实际上并不存在作为一个单独的集合类型。Python中的集合(set)类型是可变的,这意味着你可以在创建集合之后添加、删除或修改集合中的元素。
然而,如果你需要一个不可变的集合,你可以使用Python中的frozenset
类型。frozenset
是一个不可变的集合,一旦创建,其内容就不能被修改。
1.2 不可变集合特性
- **不可变性:**不可变集合一旦创建,其元素就不能被修改。这意味着你不能向不可变集合中添加新元素,也不能删除或更改现有元素。如果尝试这样做,Python会抛出一个
TypeError
异常。 - **哈希性:**由于不可变集合是哈希不可变的,因此它们可以用作字典的键或集合的元素。这在需要唯一标识符或需要快速查找的情况下非常有用。
- **性能:**在某些情况下,
frozenset
可能比set
更快,因为不可变性允许更多的优化。
二、创建不可变集合
2.1 使用大括号创建
创建一个空的不可变集合非常简单,只需使用带有frozenset()
的大括号即可,但需要注意,大括号本身是用来创建可变集合的,因此直接写{}
会得到一个可变集合。为了创建空的不可变集合,应该这样写:
empty_frozenset = frozenset()
print(empty_frozenset) # frozenset()
print(type(empty_frozenset)) # <class 'frozenset'>
代码运行结果如下图所示:
2.2 使用frozenset()
函数创建
要将任何可迭代对象(如列表、元组、集合等)转换为不可变集合,可以使用frozenset()
函数。例如:
# 使用frozenset()函数将字符串转换为不可变集合
frozenset01 = frozenset("abcdefg")
print(frozenset01) # frozenset({'g', 'f', 'b', 'd', 'c', 'a', 'e'})
print(type(frozenset01)) # <class 'frozenset'>
# 使用frozenset()函数将列表转换为不可变集合
frozenset02 = frozenset([10,20,30,40,30])
print(frozenset02) # frozenset({40, 10, 20, 30})
print(type(frozenset02)) # <class 'frozenset'>
# 使用frozenset()函数将元组转换为不可变集合
frozenset03 = frozenset((10,20,30,40))
print(frozenset03) # frozenset({40, 10, 20, 30})
print(type(frozenset03)) # <class 'frozenset'>
# 使用frozenset()函数将字典转换为不可变集合,将字典的键存储到不可变集合中
frozenset04 = frozenset({"a":100,"b":200})
print(frozenset04) # frozenset({'b', 'a'})
print(type(frozenset04)) # <class 'frozenset'>
代码运行结果如下图所示:
三、删除不可变集合
当不可变集合不再使用时,使用del命令删除整个UK恶变集合。删除不可变集合后,再次使用不可变集合会报NameError错误。
frozenset01 = frozenset([10,20,30,40,50])
print(frozenset01) # frozenset({40, 10, 50, 20, 30})
print(type(frozenset01))
del frozenset01
print(frozenset01) # NameError: name 'frozenset01' is not defined
代码执行效果如下:
四、访问不可变集合元素
在Python中,不可变集合(frozenset
)是一种无序且不包含重复元素的数据结构。因此,你不能像访问列表(list
)或元组(tuple
)那样通过索引来获取不可变集合中的某一个特定元素。不过,你可以使用一些方法来获取不可变集合中的元素,例如通过迭代、随机选择等。
4.1 迭代集合
使用循环来遍历不可变集合中的元素。
forzenset01 = frozenset([10,20,30,40,50])
print(forzenset01)
print(type(forzenset01))
for element in forzenset01:
print(element) # 逐个打印集合中的元素
运行结果如下图所示:
4.2 转换为列表并使用索引
将不可变集合转换为列表,然后使用索引来访问元素。不过,这种方法并不推荐,因为它失去了不可变集合无序且唯一的特性。
frozenset = frozenset((10, 20, 30, 40, 50, 60, 70, 80, 90))
my_list = list(frozenset)
print(my_list[0]) # 访问转换为列表后的a第一个元素(注意,这个元素不一定是最初插入的那个) 我这里第一次运行输出的是70,再次运行输出的可能就是其他元素
运行结果如下图所示:
每种方法都有其适用的场景,选择哪种方法取决于你的具体需求。
五、遍历不可变集合元素
在Python中,不可变集合是一种无序且不重复的元素集合。要遍历一个集合,可以使用多种方法,包括使用for
循环、列表推导式等。以下是一些常用的遍历不可变集合的方法
5.1 使用 for
循环遍历不可变集合
这是最直接和常用的方法。
frozenset01 = frozenset((10, 20, 30, 40, 50, 60, 70, 80, 90))
for element in frozenset01:
print(element,end=" ")
运行结果如下图所示:
5.2 使用enumerate()函数遍历不可变集合
可以使用enumerate()函数和for循环遍历不可变集合元素
frozenset01 = frozenset((10, 20, 30, 40, 50, 60, 70, 80, 90))
for index,value in enumerate(frozenset01):
print(f"第{index}个元素:{value}")
运行结果如下图所示:
5.3 使用列表推导式遍历不可变集合
虽然列表推导式主要用于生成列表,但你也可以用它来遍历不可变集合,并在遍历过程中将元素收集到一个列表中。
frozenset01 = frozenset((10, 20, 30, 40, 50, 60, 70, 80, 90))
# 将不可变集合中的每个元素收集到一个列表中
squared_elements = [element for element in frozenset01]
print(squared_elements)
运行结果如下图所示:
六、查找不可变集合元素
6.1 in运算符查询元素在不可变集合中是否存在
使用in运算符查询某个元素在集合中是否存在,如果存在返回True,如果不在返回False。
语法格式如下:
result = 元素值 in 不可变集合名
演示代码如下:
frozenset01 = frozenset((10, 20, 30, 40, 50, 60, 70, 80, 90))
result01 = 10 in frozenset01
print("元素10在不可变集合frozenset01中存在:",result01) # 元素10在不可变集合frozenset01中存在: True
result02 = 100 in frozenset01
print("元素100在不可变集合frozenset01中存在:",result02) # 元素100在不可变集合frozenset01中存在: False
运行结果如下图所示:
6.2 not in运算符查询元素在不可变集合中是否不存在
使用not in运算符查询某个元素在不可变集合中是否不存在,如果不存在返回True,如果存在返回False。
语法格式如下:
result = 元素值 not in 不可变集合名
演示代码如下:
frozenset01 = frozenset((10, 20, 30, 40, 50, 60, 70, 80, 90))
result01 = 10 not in frozenset01
print("元素10不在不可变集合frozenset01中存在:",result01) # 元素10不在不可变集合frozenset01中存在: False
result02 = 100 not in frozenset01
print("元素100不在不可变集合frozenset01中存在:",result02) # 元素100不在不可变集合frozenset01中存在: True
运行结果如下图所示:
七、集合推导式
7.1 什么是推导式
Python推导式(comprehension)是一种简洁而高效的构建容器(如列表、集合、字典)的方法。它允许你用一行代码来创建容器,并且可以通过循环和条件语句来生成容器中的元素。
推导式分为列表推导式(list comprehension)、集合推导式(set comprehension)、字典推导式(dictionary comprehension)以及生成器表达式(generator expression)。
推导式是Python中非常强大和常用的特性,可以使代码更加简洁和高效。
7.2 不可变集合推导式
不可变集合推导式提供了一种创建列表的简洁方法,通常是操作某个序列的每个元素,并将其结果作为新不可变集合的元素,或者根据判定条件创建子序列。不可变集合推导式一般由表达式及for语句构成,其后还可以有零个到多个for自居或if子句,返回结果是表达式在for语句和if语句的操作下生成的不可变集合。
语法格式:
frozensetname = frozenset(expression for variable in 对象(if condition))
- frozensetname:新生成的不可变集合名字。
- expression:表达式。
- variable:变量名。
- (if condition):用于从对象中选择符合要求的不可变集合。
演示示例:
# 1:生成[0-10)之间的10个整数,存储在不可变集合中
# 普通方式生成
list01 = []
for i in range(10):
list01.append(i)
frozenset01 = frozenset(list01)
print(type(frozenset01)) # print(type(frozenset01))
print("普通方式生成的不可变集合:",frozenset01) # 普通方式生成的不可变集合: frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
# 不可变集合推导式生成
frozenset02 = frozenset(i for i in range(10))
print(type(frozenset02)) # <class 'frozenset'>
print("不可变集合推导式生成的不可变集合:",frozenset02) # 不可变集合推导式生成的不可变集合: frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
# 2:生成一个[1-10]之间所有整数的平方组成的不可变集合
# 普通方式生成
list02 = []
for i in range(1,11):
list02.append(i ** 2)
frozenset03 = frozenset(list02)
print(type(frozenset03)) # <class 'frozenset'>
print("普通方式生成的不可变集合:",frozenset03) # 普通方式生成的不可变集合: frozenset({64, 1, 4, 36, 100, 9, 16, 49, 81, 25})
# 不可变集合推导式生成
frozenset04 = frozenset(i**2 for i in range(1,11))
print(type(frozenset04)) # 普通方式生成的不可变集合: frozenset({64, 1, 4, 36, 100, 9, 16, 49, 81, 25})
print("不可变集合推导式生成的不可变集合:",frozenset04) # 不可变集合推导式生成的不可变集合: frozenset({64, 1, 4, 36, 100, 9, 16, 49, 81, 25})
# 3:取出已有不可变集合中的奇数值生成新不可变集合
# 已有不可变集合
frozenset05 = frozenset([1,2,3,4,5,6,7,8,9,10])
# 普通方式生成
list03 = []
for i in frozenset05:
if i%2!=0:
list03.append(i)
frozenset06 = frozenset(list03)
print(type(frozenset06)) # <class 'frozenset'>
print("普通方式生成的不可变集合:",frozenset06) # 普通方式生成的不可变集合: frozenset({1, 3, 5, 7, 9})
# 不可变集合推导式生成
frozenset07 = frozenset(i for i in frozenset05 if i%2!=0)
print(type(frozenset07)) # <class 'frozenset'>
print("不可变集合推导式生成的不可变集合:",frozenset07) # 不可变集合推导式生成的不可变集合: frozenset({1, 3, 5, 7, 9})
运行结果如下图所示:
八、集合运算
集合运算是指对集合进行的一系列操作,如并集、交集、差集等。这些运算在数据处理、逻辑运算等领域有着广泛的应用。
以下是Python集合的运算操作符或方法的展示
运算类型 | 操作符 | 方法 | 说明 |
---|---|---|---|
并集 | `A | B` | A.union(B) |
交集 | A&B | A.intersection(B) | 交集,返回一个新集合,包括同时在集合A和集合B中的元素 |
差集 | A-B | A.difference(B) | 差集,返回一个新集合,包括在集合A中但不在集合B中的元素 |
对称差集 | A^B | A.symmetric_difference(B) | 对称差集,返回一个新集合,包括集合A和集合B中的元素,但是不包括同时在两集合中的元素 |
子集 | A<=B | A.issubset(B) | 如果集合A与集合B相同或者A是B的子集,返回True,否则返回False |
真子集 | A<B | 无 | 如果集合A是集合B的真子集,返回True,否则返回False |
超集 | A>=B | A.issuperset(B) | 如果集合A与集合B相同或者A是B的超集,返回True,否则返回False |
真超集 | A>B | 无 | 如果集合A是集合B的真超集,返回True,否则返回False |
集合相等 | A==B | 无 | 如果集合A与集合B相同,返回True,否则返回False |
集合不相等 | A!=B | 无 | 如果集合A与集合B不 相同,返回True,否则返回False |
空交集判断 | 无 | A.isdisjoint(B) | 如果集合A与集合B没有公共元素,返回 True,否则返回 False。 |
更新 | `A | =B` | A.update(B) |
更新 | 无 | intersection_update() | 用自己和另一个的交集来更新这个集合 |
更新 | 无 | difference_update() | 从这个集合中删除另一个集合的所有元素 |
更新 | 无 | symmetric_difference_update() | 用自己和另一个的对称差来更新这个集合 |
集合的并集、交集、差集、对称差集运算代码:
# 在对集合做运算时,不会影响原来的集合,而是返回一个运算结果
# 创建2个集合
A = frozenset({10,20,30,40,50})
B = frozenset({30,40,50,60,70})
print(type(A)) # <class 'frozenset'>
print(type(B)) # <class 'frozenset'>
print("运算前集合A:",A) # 运算前集合A: frozenset({50, 20, 40, 10, 30})
print("运算前集合B:",B) # 运算前集合B: frozenset({50, 70, 40, 60, 30})
# 并集:使用 A | B 或 A.union(B)实现,返回一个新集合,包括集合A和集合B中所有的元素。
result01 = A | B
print("并集 A | B:",result01) # 并集 A | B: frozenset({70, 40, 10, 50, 20, 60, 30})
result02 = A.union(B)
print("并集 A.union(B):",result02) # 并集 A.union(B): frozenset({70, 40, 10, 50, 20, 60, 30})
# 交集:使用 A & B 或 A.intersection(B)实现返回一个新集合,包括同时在集合A和集合B中的元素。
result03 = A & B
print("交集 A & B:",result03) # 交集 A & B: frozenset({40, 50, 30})
result04 = A.intersection(B)
print("交集 A.intersection(B):",result04) # 交集 A.intersection(B): frozenset({40, 50, 30})
# 差集:使用 A - B 或 A.difference(B)实现返回一个新集合,包括在集合A中但不在集合B中的元素。
result05 = A - B
print("差集 A - B:",result05) # 交集 A.intersection(B): frozenset({40, 50, 30})
result06 = A.difference(B)
print("差集 A.difference(B):",result06) # 交集 A.intersection(B): frozenset({40, 50, 30})
# 对称差集:使用 A ^ B 或 A.symmetric_difference(B)实现返回一个新集合,包括集合A和集合B中的元素,但是不包括同时在两集合中的元素。
result07 = A ^ B
print("对称差集 A ^ B:",result07) # 对称差集 A ^ B: frozenset({20, 70, 10, 60})
result08 = A.symmetric_difference(B)
print("对称差集 A.symmetric_difference(B):",result08) # 对称差集 A.symmetric_difference(B): frozenset({20, 70, 10, 60})
# 做完并集、差集、交集、对称差集运算之后的集合A和集合B
print("运算后集合A:",A) # 运算后集合A: frozenset({50, 20, 40, 10, 30})
print("运算后集合B:",B) # 运算后集合B: frozenset({50, 70, 40, 60, 30})
集合的子集、真子集、超集、真超集运算代码:
# 在对不可变集合做运算时,不会影响原来的不可变集合,而是返回一个运算结果
# 创建3个不可变集合
A1 = frozenset({10,20,30})
B1 = frozenset({10,20,30})
B2 = frozenset({10,20,30,40,50})
print(type(A1)) # <class 'frozenset'>
print(type(B1)) # <class 'frozenset'>
print(type(B2)) # <class 'frozenset'>
print("运算前不可变集合A1:",A1) # 运算前不可变集合A1: {10, 20, 30}
print("运算前不可变集合B1:",B1) # 运算前不可变集合B1: {10, 20, 30}
print("运算前不可变集合B2:",B2) # 运算前不可变集合B2: {50, 20, 40, 10, 30}
# 子集:使用 A <= B 或 A.issubset(B)实现,如果不可变集合A中的元素全部都在不可变集合B中出现,那么不可变集合A就是不可变集合B的子集,返回True,否则返回False
# 超集:使用 A >= B 或 A.issuperset(B)实现,如果不可变集合B中的元素全部都在不可变集合A中,那么不可变集合A就是不可变集合B的超集,返回True,否则返回False
# 真子集:特殊的子集,使用 A < B 或 实现,如果超集B中含有子集A中所有元素,并且不可变集合B中还有不可变集合A中没有的元素,那么不可变集合A是不可变集合B的真子集,返回True,否则返回False
# 真超集:特殊的超集,使用A > B 或 实现,如果超集A中包含有自己B中所有㢝,并且不可变集合A中还有不可变集合B中没有的元组,那么不可变集合A是不可变集合B的真超集,返回True,否则返回False
print("不可变集合A1是不可变集合B1的子集:",A1 <= B1) # 不可变集合A1是不可变集合B1的子集: True
print("不可变集合A1是不可变集合B1的真子集:",A1 < B1) # 不可变集合A1是不可变集合B1的真子集: False
print("不可变集合A1是不可变集合B2的子集:",A1 <= B2) # 不可变集合A1是不可变集合B2的子集: True
print("不可变集合A1是不可变集合B2的真子集:",A1 < B2) # 不可变集合A1是不可变集合B2的真子集: True
print("不可变集合B1是不可变集合A1的超集:",B1 >= A1) # 不可变集合B1是不可变集合A1的超集: True
print("不可变集合B1是不可变集合A1的真超集:",B1 > A1) # 不可变集合B1是不可变集合A1的真超集: False
print("不可变集合B2是不可变集合A1的超集:",B2 >= A1) # 不可变集合B2是不可变集合A1的超集: True
print("不可变集合B2是不可变集合A1的真超集:",B2 > A1) # 不可变集合B2是不可变集合A1的真超集: True
运算结果如下图所示:
集合相等和不相等判断、集合空交集判断代码:
# 在对不可变集合做运算时,不会影响原来的不可变集合,而是返回一个运算结果
# 创建两个不可变集合
A = frozenset({10,20,30})
B1 = frozenset({10,20,30})
B2 = frozenset({40,50,60})
# 不可变集合相等判断、不可变集合不相等判断:使用==和!=实现
print("不可变集合A=不可变集合B1:",A == B1) # 不可变集合A=不可变集合B1: True
print("不可变集合A=不可变集合B2:",A == B2) # 不可变集合A=不可变集合B2: False
print("不可变集合A!=不可变集合B1:",A != B1) #不可变集合A!=不可变集合B1: False
print("不可变集合A!=不可变集合B2:",A != B2) # 不可变集合A!=不可变集合B2 True
# 不可变集合空交集判断:
print("不可变集合A和不可变集合B1没有交集:",A.isdisjoint(B1)) # 不可变集合A和不可变集合B1没有交集: False
print("不可变集合A和不可变集合B2没有交集:",A.isdisjoint(B2)) # 不可变集合A和不可变集合B2没有交集: True
集合相等和不相等判断、集合空交集判断运行结果如下:
需要注意的是,不可变集合中的元素是无序且唯一的,因此在进行不可变集合运算时,结果不可变集合中的元素顺序可能与示例中的顺序不同,但元素本身是相同的。此外,不可变集合运算的结果也是不可变集合类型,可以使用不可变集合的相关方法和操作符进行进一步的操作。
九、不可变集合其他操作
函数 | 描述 |
---|---|
all() | 如果集合中的所有元素都是 True(或者集合为空),则返回 True。 |
any() | 如果集合中的所有元素都是 True,则返回 True;如果集合为空,则返回 False。 |
max() | 返回集合中的最大项 |
min() | 返回集合中的最小项 |
sorted() | 从集合中的元素返回新的排序列表(不排序集合本身) |
sum() | 返回集合的所有元素之和 |
代码如下所示:
frozenset01 = frozenset([10,20])
frozenset02 = frozenset([0])
frozenset03 = frozenset()
frozenset04 = frozenset([False])
print(frozenset01,type(frozenset01)) # frozenset({10, 20}) <class 'frozenset'>
print(frozenset02,type(frozenset02)) # frozenset({0}) <class 'frozenset'>
print(frozenset03,type(frozenset03)) # frozenset({0}) <class 'frozenset'>
print(frozenset04,type(frozenset04)) # frozenset({False}) <class 'frozenset'>
# all():如果不可变集合中的所有元素都是 True(或者不可变集合为空),则返回 True。
print("all():",all(frozenset01)) # all(): True
print("all():",all(frozenset02)) # all(): False
print("all():",all(frozenset03)) # all(): True
print("all():",all(frozenset04)) # all(): False
# any():如果不可变集合中的所有元素都是 True,则返回 True;如果不可变集合为空,则返回 False。
print("any():",any(frozenset01)) # any(): True
print("any():",any(frozenset02)) # any(): False
print("any():",any(frozenset03)) # any(): False
print("any():",any(frozenset04)) # any(): False
# max():返回不可变集合中的最大项
print("max():",max(frozenset01)) # max(): 20
# min():返回不可变集合中的最小项
print("min():",min(frozenset01)) # min(): 10
# sum():返回不可变集合的所有元素之和
print("sum():",sum(frozenset01)) # sum(): 30
# sorted() 从不可变集合中的元素返回新的排序列表(不排序不可变集合本身)
frozenset05 = frozenset({10,40,90,20,30,50,80,60,70})
print(frozenset05) # frozenset({70, 40, 10, 80, 50, 20, 90, 60, 30})
print(type(frozenset05)) # <class 'frozenset'>
# sorted()默认进行升序排序,返回一个升序排序的列表
result01 = sorted(frozenset05)
print(result01) # [10, 20, 30, 40, 50, 60, 70, 80, 90]
print(type(result01)) # <class 'list'>
result02 = sorted(frozenset05,reverse=True)
print(result02) # [90, 80, 70, 60, 50, 40, 30, 20, 10]
print(type(result02)) # <class 'list'>
运行结果如下图所示:
十、可变集合和不可变集合对比
Python中的可变集合和不可变集合存在显著的异同点。以下是详细的对比分析:
10.1 相同点
-
集合特性:
- 无论是可变集合还是不可变集合,都具有集合的基本特性,即元素是无序的、唯一的,且不支持通过索引访问元素。
- 两者都支持集合操作,如并集、交集、差集等。
-
元素类型:
- 可变集合和不可变集合都可以包含各种数据类型,如数字、字符串、元组等,但需要注意的是,不可变集合(frozenset)本身作为元素时,其内部元素也必须是不可变的。
10.2 不同点
-
可变性:
- 可变集合:允许在创建后修改其内容,即可以添加或删除元素。这种修改是在原地进行的,不会创建新的对象。
- 不可变集合:一旦创建,其内容就不能被添加或删除。如果尝试修改,Python会创建一个新的对象来存储新的值,而不是改变原有的对象。
-
哈希性与字典键:
- 可变集合:由于其内容可以改变,因此不能用作字典的键。这是因为字典是基于哈希表实现的,而哈希表要求键的值在插入后不能改变。
- 不可变集合:由于其内容不可变,因此可以用作字典的键。
-
操作与异常:
- 对于可变集合,可以使用如
add()
、remove()
、discard()
、pop()
等方法来添加或删除元素。如果尝试删除不存在的元素,remove()
方法会抛出KeyError
异常,而discard()
方法则不会抛出异常。 - 对于不可变集合,由于其内容不可变,因此不能使用上述方法来修改集合。任何尝试修改不可变集合的操作都会导致新的
frozenset
对象的创建。
- 对于可变集合,可以使用如
-
内存与性能:
- 不可变集合由于其内容不可变,因此可以提供数据的安全性,并有助于避免在程序运行时出现难以调试的错误。此外,不可变类型通常可以更有效地被优化,因为它们的值不会改变,这可以带来性能上的好处。
- 可变集合则更加灵活,允许在程序运行时动态地修改集合的内容。然而,这种灵活性也可能导致意外的副作用,特别是在函数参数传递等场景中。
综上所述,Python中的可变集合和不可变集合在可变性、哈希性与字典键、操作与异常以及内存与性能等方面存在显著的异同点。在选择使用哪种集合时,应根据具体的应用场景和需求进行权衡。
十一、总结
不可变集合是Python中的一种有用数据结构,它提供了集合的许多优点(如快速查找和唯一性),同时由于其不可变性,还可以用作字典的键或集合的元素。本文介绍了如何创建不可变集合、其特性和操作,希望能够帮助你更好地理解和使用不可变集合。