# ------------------------------------集合论------------------------------------
"""
"集"这个概念在Python中算是比较年轻的,同时它的使用率也比较低.set和它的不可变是姊妹类型frozenset直到Python2.3才首次以模块的形式出现,
然后在Python2.6中它们升级成为内置类型.
"""
# 集合的本质是许多唯一对象的聚集.因此,集合可以用于去重.
l1 = ['spam', 'spam', 'eggs', 'spam']
s1 = set(l1)
print(s1) # {'spam', 'eggs'}
print(list(s1)) # ['spam', 'eggs']
# 利用这些运算符可以省去不必要的循环和逻辑操作
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
print(s1 | s2) # {1, 2, 3, 4, 5, 6} 合集
print(s1 & s2) # {3, 4} 交集
print(s1 - s2) # {1, 2} 差集
# needles的元素在haystack里出现的次数,两个变量都是set类型
needles = {1, 2, 3, 4}
haystack = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
found = len(needles & haystack) # 交集 这比下面的形式速度要快一点
print(found)
found = 0
for n in needles:
if n in haystack:
found += 1
# 就算手头没有集合,我们也可以随时建立集合
found = len(set(needles) & set(haystack))
found2 = len(set(needles).intersection(haystack))
"""
除了速度极快的查找功能(这也得归功于它背后的散列表),内置的set和frozenset提供了丰富的功能和操作,不但让创建集合的方式丰富多彩,
而且对于set来讲,我们还可以对集合里已有的元素进行修改.
"""
# ------------------------------------集合字面量------------------------------------
"""
除空集之外,集合的字面量---{1} {1, 2},等等---看起来跟它的数学形式一样.如果是空集,那么必须写成set()的形式.
不要忘了,如果要创建一个空集,你必须用不带任何参数的构造方法set().
如果只是写成{}的形式,那你创建的其实是个空字典.
像{1, 2, 3}这种字面量语法相比于构造方法set([1, 2, 3])要更快且更易读.后者的速度要慢一些,因为Python必须先从set这个名字来查询构造方法,
然后新建一个列表,最后再把这个列表传入到构造方法里.但是如果是像{1, 2, 3}这样的字面量,Python会利用一个专门的叫做BUILD_SET的字节码来创建集合.
由于Python里没有针对frozenset的特殊字面量句法,我们只能采用构造方法.Python3里frozenset的标准字符串表示形式看起来就像构造方法调用一样.
"""
f1 = frozenset(range(10))
print(f1) # frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
# ------------------------------------集合推导------------------------------------
"""
1.从unicodedata模块里导入name函数,用以获取字符的名字.
2.把编码在32-255之间的字符的名字里有'SIGN'单词的挑出来,放到一个集合里.
"""
from unicodedata import name
name1 = {chr(i) for i in range(32, 256) if 'SIGN' in name(chr(i), '')}
print(name1)
# ------------------------------------集合的操作------------------------------------
"""
中缀运算符需要两侧的被操作对象都是集合类型,但是其他的所有方法则只要所传入的参数四可迭代对象.
例如,想求4个聚合类型a,b,c,d的合集,可以用a.union(b, c, d),这里a必须是个set,但是b,c,d则可以是任何类型的可迭代对象.
"""
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
l3 = [3, 4, 5, 6]
print(s1.union(s2, l3)) # {1, 2, 3, 4, 5, 6}
"""
集合的数学运算: 这些方法或者会生成新集合,或者会在条件允许的情况下就地修改集合
"""
# S ∩ Z 交集
print(s1 & s2) # s1.__add__(s2)
print(s2 & s1) # s1.__rand__(s2)
print(s1.intersection(s2))
s1 &= s2 # s1.__iand__(s2) 就地更新
print(s1)
s1.intersection_update(s2) # 等同s1 &= s2
# S ∪ Z 并集
print(s1 | s2) # s1.__or__(s2)
print(s2 | s1) # s1.__oro__(s2)
print(s1.union(s2))
s1 |= s2 # s1.__ior__(s2) 就地更新
s1.update(s2) # 就地更新
# S \ Z 差集
print(s1 - s2) # s1.__sub__(s2)
print(s2 - s1) # s1.__rsub__(s2)
print(s1.difference(s2))
s1 -= s2 # s1.__isub__(s2) 就地更新
s1.difference_update(s2) # 就地更新
# S ▲ Z 对称差集
print(s1 ^ s2) # s1.__xor__(s2)
print(s2 ^ s1) # s1.__rxor__(s2)
print(s1.symmetric_difference(s2))
s1 ^= s2 # s1.__ixor__(s2) 就地更新
s1.symmetric_difference_update(s2) # 就地更新
"""
集合的比较运算符,返回值是布尔类型
"""
s1.isdisjoint(s2) # 查看s1和s2是否不相交(没有共同元素)
# e ∈ s1
e = 1
print(e in s1) # s1.__contains__(e)
# S1 ⊆ S2
print(s1 <= s2) # s1.__le__(s2)
s1.issubset(s2) # 把可迭代对象转化为集合,然后查看s1是否是它的子集
# S1 ⊊ S2
print(s1 < s2) # s1.__lt__(s2)
# S1是否S2的父集
print(s1 >= s2) # s1.__ge__(s2)
s1.issuperset(s2) # 可以使用可迭代对象
# S1是否S2的真父集
print(s1 > s2) # s1.__gt__(s2)
"""
集合类型的其他方法
"""
s1.add(7)
s1.clear()
s3 = s1.copy()
s1.discard(1) # 如果s1中有1这个元素的话,把它移除
s1.pop() # 从s1中移除一个元素并返回它的值,若s1为空,抛出KeyError异常
s1.remove(1) # 从s1中移除1,若1不存在,抛出KeyError异常
s1.__iter__() # 返回s1的迭代器
s1.__len__() # len(s1)