集合
集合是一组没有重复、也没有顺序的值。把各项写在花括号里,像一个没有 : 值 的字典。同一个值放两次,集合只留一个。
1lit = {3, 1, 4, 1, 5}2print(lit)3print(len(lit))4print(type(lit))
{1, 3, 4, 5}
4
<class 'set'>
这个集合有四项,不是五项:第二个 1 被扔掉了。今晚点亮了哪些灯,就很适合用集合来记:一盏灯要么亮着要么没亮,点两次也不算两盏。
去掉重复
set() 把列表(或者字符串、元组)变成集合,所有重复的都会去掉。想知道「一共有哪几种不同的值」,这是最快的办法。
1visits = [2, 5, 2, 7, 5, 2]2print(set(visits))3print(len(set(visits)))4ore = ["铁", "煤", "铁", "金", "煤"]5print(sorted(set(ore)))6print(sorted(set("banana")))
{2, 5, 7}
3
['煤', '金', '铁']
['a', 'b', 'n']
想再变回列表,用 sorted(...) 得到排好序的列表,或者用 list(...)。空集合写成 set():单写 {} 是一个空字典。
没有顺序,也没有下标
集合不会按你写的顺序存东西,你看到的顺序也靠不住。如果里面是字符串,每次运行程序,顺序都可能不一样。所以这一课里,字符串的集合都用 sorted() 来显示。
没有顺序,也就没有「第 0 项」:
1lit = {3, 1, 4}2print(4 in lit)3print(lit[0])
True
Traceback (most recent call last):
File "main.py", line 3, in <module>
print(lit[0])
TypeError: 'set' object is not subscriptable发生了什么: 这种东西不能用 [ ] 取里面的东西。
小技巧: 要顺序、要位置,用列表。只关心「在不在」和「不重复」,用集合。
添加和删除
add 放进一项;放一个已经有的,什么也不会发生。remove 拿掉一项,不在就报 KeyError。discard 也是拿掉一项,不在也不出声。
1lit = {1, 2}2lit.add(3)3lit.add(2)4print(lit)5lit.remove(1)6print(lit)7lit.discard(9)8print(lit)9lit.remove(9)
{1, 2, 3}
{2, 3}
{2, 3}
Traceback (most recent call last):
File "main.py", line 9, in <module>
lit.remove(9)
KeyError: 9发生了什么: 字典里没有 9 这个键。
update 把一个列表或另一个集合里的每一项都加进来。clear() 把集合清空。
快速的 in
列表也能用 in,但列表得从头一项一项地查。集合能直接找到该去的地方,所以哪怕有一百万项,集合上的 in 也很快。程序要一遍遍地问「这个见过没有」时,就用集合。
1seen = {"磨坊巷", "池塘路"}2print("池塘路" in seen)3print("山坡街" in seen)4print("山坡街" not in seen)5seen.add("山坡街")6print(len(seen))
True False True 3
not in 和 in 正好相反,读起来像一句英语;列表、字符串、字典上也都能用。
并集、交集、差集
集合可以像图里的几个圆圈那样组合。每种运算都造出一个新集合,原来的两个不变。
1mon = {1, 2, 3, 4}2tue = {3, 4, 5}3print(mon | tue)4print(mon & tue)5print(mon - tue)6print(tue - mon)7print(mon ^ tue)
{1, 2, 3, 4, 5}
{3, 4}
{1, 2}
{5}
{1, 2, 5}
| 写法 | 方法 | 得到 | 在这里是 |
|---|---|---|---|
a | b | a.union(b) | 在 a 里或在 b 里(或两边都在) | 两天里任一天亮过的灯 |
a & b | a.intersection(b) | 只要两边都有的 | 两天都亮过的灯 |
a - b | a.difference(b) | 在 a 里、不在 b 里 | 只有周一亮过的灯 |
a ^ b | a.symmetric_difference(b) | 只在一边的 | 只有其中一天亮过的灯 |
这段会显示什么?
1a = {1, 2, 3}2b = {2, 3, 4}3print(len(a | b), len(a & b))
a | b 是 {1, 2, 3, 4}:2 和 3 两边都有,但只算一次,所以是四项。a & b 是 {2, 3},两项。
方法和符号的作用一样。有一点不同:方法还能接受列表,所以 a.union([7, 8]) 可以,a | [7, 8] 会报错。
子集
a <= b 问的是「a 的每一项是不是都在 b 里」,和 a.issubset(b) 一样。== 在两个集合的项完全相同时为 True,不管你写的顺序是什么。
1needed = {"灯", "油"}2bag = {"油", "地图", "灯"}3print(needed <= bag)4print(needed.issubset({"油"}))5print({1, 2, 3} == {3, 2, 1})6print(sorted(bag - needed))
True False True ['地图']
方法一览
| 写法 | 作用 |
|---|---|
set(x) | 由 x 里的项组成的集合,去掉重复 |
s.add(x) | 加上 x |
s.update(x) | 加上 x 里的每一项 |
s.remove(x) | 拿掉 x(没有就报 KeyError) |
s.discard(x) | 拿掉 x,有的话 |
s.clear() | 全部拿掉 |
x in s | x 在不在集合里(很快) |
a | b、a & b、a - b、a ^ b | 并集、交集、差集、对称差 |
a <= b / a.issubset(b) | a 的每一项是不是都在 b 里 |
练一练
写好代码,点检查。提示随便看,实在想不出来再看答案。
有几户
✓显示列表里有几个不同的门牌号。
4
1visits = [12, 4, 12, 7, 4, 12, 9]2# 在这里写
还黑着的灯
✓all_lamps 是镇上所有的灯,lit 是 Wick 已经点亮的。把还黑着的灯排好序,作为列表显示出来。
[1, 3, 4]
1all_lamps = {1, 2, 3, 4, 5, 6}2lit = {2, 5, 6}3# 在这里写
两座矿都有的
✓把两座矿里都挖到过的矿石种类,排好序,作为列表显示出来。
['煤', '铁']
1north = ["铁", "煤", "金", "铁"]2south = ["煤", "铜", "铁"]3# 在这里写
这一课学会了
- 集合里的值不重复、没有顺序:
{1, 2, 3}。空集合是set()。 set(列表)去掉重复;sorted(set(...))把它们变回排好序的列表。- 没有顺序就没有下标:
s[0]会报错。 add、update、remove、discard改集合;集合上的in很快。|、&、-、^组合集合;<=判断子集。