集合论

高效编写 Python 代码

Logan Thomas

Scientific Software Technical Trainer, Enthought

集合论

  • 研究对象集合的数学分支
    • sets
  • Python 内置 set 数据类型及其方法:
    • intersection(): 同时属于两个集合的元素
    • difference(): 属于一个集合但不属于另一个的元素
    • symmetric_difference(): 只属于其中一个集合的元素
    • union(): 属于任一集合的元素
  • 成员检测很快
    • 检查值是否在序列中
    • 使用 in 运算符
高效编写 Python 代码

用循环比较对象

list_a = ['Bulbasaur', 'Charmander', 'Squirtle']
list_b = ['Caterpie', 'Pidgey', 'Squirtle']

alt="宝可梦妙蛙种子、⼩火龙和杰尼龟在名为列表 A 的框中,绿毛虫、波波和杰尼龟在名为列表 B 的框中"

高效编写 Python 代码

用循环比较对象

list_a = ['Bulbasaur', 'Charmander', 'Squirtle']
list_b = ['Caterpie', 'Pidgey', 'Squirtle'] 

alt="宝可梦妙蛙种子、⼩火龙和杰尼龟在名为列表 A 的框中,绿毛虫、波波和杰尼龟在名为列表 B 的框中;两框内的杰尼龟均被圈出"

高效编写 Python 代码
list_a = ['Bulbasaur', 'Charmander', 'Squirtle']
list_b = ['Caterpie', 'Pidgey', 'Squirtle'] 
in_common = []

for pokemon_a in list_a:
    for pokemon_b in list_b:
        if pokemon_a == pokemon_b:
            in_common.append(pokemon_a)

print(in_common)
['Squirtle']
高效编写 Python 代码
list_a = ['Bulbasaur', 'Charmander', 'Squirtle']
list_b = ['Caterpie', 'Pidgey', 'Squirtle'] 
set_a = set(list_a)
print(set_a)
{'Bulbasaur', 'Charmander', 'Squirtle'}
set_b = set(list_b)
print(set_b)
{'Caterpie', 'Pidgey', 'Squirtle'}
set_a.intersection(set_b)
{'Squirtle'}
高效编写 Python 代码

集合论带来的效率提升

%%timeit
in_common = []

for pokemon_a in list_a:
    for pokemon_b in list_b:
        if pokemon_a == pokemon_b:
            in_common.append(pokemon_a)
601 ns ± 17.1 ns 每次循环(7 次运行的平均值 ± 标准差,每次 1000000 次循环)
%timeit in_common = set_a.intersection(set_b)
137 ns ± 3.01 ns 每次循环(7 次运行的平均值 ± 标准差,每次 10000000 次循环)
高效编写 Python 代码

集合方法:difference(差集)

set_a = {'Bulbasaur', 'Charmander', 'Squirtle'}
set_b = {'Caterpie', 'Pidgey', 'Squirtle'}
set_a.difference(set_b)
{'Bulbasaur', 'Charmander'}

alt="宝可梦妙蛙种子、⼩火龙和杰尼龟在名为集合 A 的框中,绿毛虫、波波和杰尼龟在名为集合 B 的框中;集合 A 框内的妙蛙种子和⼩火龙被圈出"

高效编写 Python 代码

集合方法:difference(差集)

set_a = {'Bulbasaur', 'Charmander', 'Squirtle'}
set_b = {'Caterpie', 'Pidgey', 'Squirtle'}
set_b.difference(set_a)
{'Caterpie', 'Pidgey'}

alt="宝可梦妙蛙种子、⼩火龙和杰尼龟在名为集合 A 的框中,绿毛虫、波波和杰尼龟在名为集合 B 的框中;集合 B 框内的绿毛虫和波波被圈出"

高效编写 Python 代码

集合方法:symmetric_difference(对称差)

set_a = {'Bulbasaur', 'Charmander', 'Squirtle'}
set_b = {'Caterpie', 'Pidgey', 'Squirtle'}
set_a.symmetric_difference(set_b)
{'Bulbasaur', 'Caterpie', 'Charmander', 'Pidgey'}

alt="宝可梦妙蛙种子、⼩火龙和杰尼龟在名为集合 A 的框中,绿毛虫、波波和杰尼龟在名为集合 B 的框中;妙蛙种子、⼩火龙、绿毛虫和波波被圈出"

高效编写 Python 代码

集合方法:union(并集)

set_a = {'Bulbasaur', 'Charmander', 'Squirtle'}
set_b = {'Caterpie', 'Pidgey', 'Squirtle'}
set_a.union(set_b)
{'Bulbasaur', 'Caterpie', 'Charmander', 'Pidgey', 'Squirtle'}

alt="宝可梦妙蛙种子、⼩火龙和杰尼龟在名为集合 A 的框中,绿毛虫、波波和杰尼龟在名为集合 B 的框中;所有宝可梦均被圈出,杰尼龟只被圈一次"

高效编写 Python 代码

用集合进行成员检测

# 三种数据结构中均含有相同的 720 个宝可梦
names_list  = ['Abomasnow', 'Abra', 'Absol', ...]
names_tuple = ('Abomasnow', 'Abra', 'Absol', ...)
names_set   = {'Abomasnow', 'Abra', 'Absol', ...}

alt="名为列表、元组、集合的三个框中分别包含宝可梦 Abomasnow、Abra、Absol"

高效编写 Python 代码

用集合进行成员检测

# 三种数据结构中均含有相同的 720 个宝可梦
names_list  = ['Abomasnow', 'Abra', 'Absol', ...]
names_tuple = ('Abomasnow', 'Abra', 'Absol', ...)
names_set   = {'Abomasnow', 'Abra', 'Absol', ...}

alt="名为列表、元组、集合的三个框中分别包含宝可梦 Abomasnow、Abra、Absol;宝可梦 Zubat 与每个框之间画有连线,表示分别进行成员检测"

高效编写 Python 代码
names_list  = ['Abomasnow', 'Abra', 'Absol', ...]
names_tuple = ('Abomasnow', 'Abra', 'Absol', ...)
names_set   = {'Abomasnow', 'Abra', 'Absol', ...}
%timeit 'Zubat' in names_list
7.63 µs ± 211 ns 每次循环(7 次运行的平均值 ± 标准差,每次 100000 次循环)
%timeit 'Zubat' in names_tuple
7.6 µs ± 394 ns 每次循环(7 次运行的平均值 ± 标准差,每次 100000 次循环)
%timeit 'Zubat' in names_set
37.5 ns ± 1.37 ns 每次循环(7 次运行的平均值 ± 标准差,每次 10000000 次循环)
高效编写 Python 代码

用集合求唯一值

# 720 个宝可梦的一次属性(primary types)
primary_types = ['Grass', 'Psychic', 'Dark', 'Bug', ...]
unique_types = []

for prim_type in primary_types:
    if prim_type not in unique_types:
        unique_types.append(prim_type)

print(unique_types)
['Grass', 'Psychic', 'Dark', 'Bug', 'Steel', 'Rock', 'Normal',
 'Water', 'Dragon', 'Electric', 'Poison', 'Fire', 'Fairy', 'Ice',
 'Ground', 'Ghost', 'Fighting', 'Flying']
高效编写 Python 代码

用集合求唯一值

# 720 个宝可梦的一次属性(primary types)
primary_types = ['Grass', 'Psychic', 'Dark', 'Bug', ...]
unique_types_set = set(primary_types)
print(unique_types_set)
{'Grass', 'Psychic', 'Dark', 'Bug', 'Steel', 'Rock', 'Normal',
 'Water', 'Dragon', 'Electric', 'Poison', 'Fire', 'Fairy', 'Ice',
 'Ground', 'Ghost', 'Fighting', 'Flying'}
高效编写 Python 代码

练习集合论吧!

高效编写 Python 代码

Preparing Video For Download...