Python集合与元组:去重利器与不可变列表,什么时候用它们?

上一篇文章讲了字典,这篇文章讲两个"配套"的数据结构——集合(set)和元组(tuple)。集合用于去重和集合运算,元组用于保护数据不被修改。它们各有各的用途,用对了能让代码更安全、更高效。


一句话概括

这篇文章解决一个问题:搞清楚集合(去重和集合运算)和元组(不可变数据)的用法和适用场景。


集合(set)

什么是集合

集合是无序不重复的元素集。用 {} 表示(但不像字典那样有键值对):

# 创建集合
fruits = {"苹果", "香蕉", "橙子", "苹果", "香蕉"}

print(fruits)  # {'苹果', '香蕉', '橙子'}(自动去重了!)

创建集合

# 直接创建
s = {1, 2, 3, 4, 5}

# 用 set() 从列表/字符串转换
s = set([1, 2, 2, 3, 3])    # {1, 2, 3}
s = set("hello")              # {'h', 'e', 'l', 'o'}(去重)

# 空集合不能用 {},要用 set()
empty = set()  # set(),不是 {}
not_empty = {1}  # {1}

集合的基本操作

添加元素:

s = {1, 2, 3}
s.add(4)      # {1, 2, 3, 4}
s.add(2)      # {1, 2, 3, 4}(重复添加无效)
s.update([5, 6, 7])  # {1, 2, 3, 4, 5, 6, 7}(添加多个)

删除元素:

s = {1, 2, 3, 4}
s.remove(3)   # {1, 2, 4}(元素不存在会报错)
s.discard(10) # {1, 2, 3, 4}(元素不存在也不报错)
s.pop()       # 删除并返回任意一个元素
s.clear()     # {}

集合运算(重点)

集合最强大的地方是支持数学集合运算:

a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}

# 并集:两个集合的所有元素
print(a | b)       # {1, 2, 3, 4, 5, 6, 7, 8}
print(a.union(b))  # 同上

# 交集:两个集合都有的元素
print(a & b)       # {4, 5}
print(a.intersection(b))  # 同上

# 差集:在a里但不在b里的元素
print(a - b)       # {1, 2, 3}
print(a.difference(b))  # 同上

# 对称差集:在a或b里,但不同时在两者里
print(a ^ b)       # {1, 2, 3, 6, 7, 8}
print(a.symmetric_difference(b))  # 同上

集合关系判断

s1 = {1, 2, 3}
s2 = {1, 2, 3, 4, 5}

# 子集:s1的所有元素都在s2里
print(s1.issubset(s2))    # True
print(s1 <= s2)           # True

# 超集:s2包含s1的所有元素
print(s2.issuperset(s1))  # True
print(s2 >= s1)           # True

# 真子集/真超集(不能相等)
print(s1 < s2)   # True(s1是s2的真子集)
print(s1 < s1)   # False(相等就不是真子集了)

元组(tuple)

什么是元组

元组是不可变的列表。创建后不能修改、添加、删除元素。

# 创建元组
t = (1, 2, 3, 4, 5)
t2 = 1, 2, 3, 4, 5  # 括号可以省略

# 单个元素的元组(必须有逗号)
t3 = (1,)  # (1,),不是 1
t4 = (1)   # 1,这是整数,不是元组

# 用 tuple() 从列表转换
t = tuple([1, 2, 3])  # (1, 2, 3)

为什么需要元组?

元组不可变,有以下几个用途:

1. 作为字典的键(因为不可变):

# 可以用元组作为键,但不能用列表
locations = {
    ("北京", "朝阳区"): "CBD",
    ("上海", "浦东区"): "陆家嘴",
}

2. 保护数据不被意外修改:

# 如果你知道某些数据不需要修改,用元组更安全
# 如果有人试图修改,会立即报错
COORDS = (40.7128, -74.0060)  # 纽约坐标,不会变

3. 函数返回值多个值时用元组:

def get_stats(numbers):
    return min(numbers), max(numbers), sum(numbers) / len(numbers)

result = get_stats([1, 2, 3, 4, 5])
print(result)  # (1, 5, 3.0)
min_val, max_val, avg_val = result  # 元组拆包

元组的基本操作

t = (1, 2, 3, 4, 5)

# 索引和切片(和列表一样)
print(t[0])    # 1
print(t[1:3])  # (2, 3)
print(t[::-1]) # (5, 4, 3, 2, 1)

# 遍历
for item in t:
    print(item)

# 元组拆包
x, y, z = (1, 2, 3)  # x=1, y=2, z=3

# 交换两个变量的值(利用元组拆包)
a, b = 10, 20
a, b = b, a  # a=20, b=10,不需要临时变量

实战练习:去重与统计

练习1:列表去重

# 原始列表(有重复)
numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5]

# 方法1:用set去重(但顺序可能改变)
unique = list(set(numbers))
print(unique)  # [1, 2, 3, 4, 5](顺序变了)

# 方法2:保持顺序的去重
def unique_with_order(lst):
    seen = set()
    result = []
    for item in lst:
        if item not in seen:
            seen.add(item)
            result.append(item)
    return result

print(unique_with_order(numbers))  # [1, 2, 3, 4, 5](顺序保持)

练习2:找出两个列表的共同元素

list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 6, 7, 8]

# 用集合的交集
common = set(list1) & set(list2)
print(common)  # {4, 5}

练习3:找出所有不重复的单词

text1 = "python is great and python is easy to learn"
text2 = "java is also great and python is widely used"

words1 = set(text1.split())
words2 = set(text2.split())

print("两篇都有的词:", words1 & words2)
print("第一篇独有的词:", words1 - words2)
print("第二篇独有的词:", words2 - words1)
print("两篇的所有词:", words1 | words2)

集合 vs 列表 vs 元组:对比

特性 列表 list 集合 set 元组 tuple
有序
可重复
可修改 ❌(不可变)
查找速度 O(n) O(1) O(n)
索引访问
用途 有序数据 去重、集合运算 不可变数据

常见问题FAQ

Q1:什么时候用列表,什么时候用集合?

  • 需要按顺序访问元素 → 列表
  • 需要快速查找、去重 → 集合
  • 数据不需要修改 → 元组

Q2:为什么元组不可变还重要?

元组不可变的好处:

  1. 线程安全:多线程环境下不会被意外修改
  2. 可作为字典键:列表不能做字典键,元组可以
  3. hashable:元组可以 hash,集合的元素可以是元组
  4. 性能稍好:不可变对象在某些场景下更快

Q3:怎么把元组转成列表?

t = (1, 2, 3)
lst = list(t)  # [1, 2, 3]
t2 = tuple(lst)  # (1, 2, 3)

注意:这是创建了新的对象,原来的元组还在。


Q4:namedtuple 是什么?

Python 标准库有一个 namedtuple,可以给元组的每个位置起名字:

from collections import namedtuple

# 定义一个命名的元组类型
Point = namedtuple('Point', ['x', 'y', 'z'])

# 创建对象
p = Point(1, 2, 3)

# 可以用名字访问,也可以用索引
print(p.x, p.y, p.z)  # 1 2 3
print(p[0], p[1], p[2])  # 1 2 3

写在最后

这篇文章讲了:

  • 集合(set):去重、集合运算(交/并/差/对称差)
  • 元组(tuple):不可变数据、作为字典键、多值返回

三个数据结构各有各的用途:

  • 列表:有序、可变、需要按索引访问
  • 集合:无序、唯一性、去重和集合运算
  • 元组:不可变、保护数据、hashable

下一篇文章:Python 函数,def参数详解、返回值、局部变量。


参考资料

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐