set(集合)
set:是python中的集合类型,元素不能有重复,且元素类型不能是可变类型。元素是无序的。{}代表空字典而不是空集合。要创建一个空集合得用set()函数
x = {42, 'foo', (1, 2, 3), 3.14159}
print(x)
x = {42, 'foo', [1, 2, 3], 3.14159}
print(x)
输出结果:
{42, 3.14159, 'foo', (1, 2, 3)}
Traceback (most recent call last):
File "F:\RolandWork\PythonProjects\studyPython\forTest.py", line 5, in <module>
x = {42, 'foo', [1, 2, 3], 3.14159}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: unhashable type: 'list'
上面结果中可以看到,集合中元素可以是多种类型,但不能是可变类型。元组可以做为集合的元素,但列表不可以。
set(iterable):通过set(iterable)函数来创建集合。创建后,可迭代对象中的重复值将被去除。
lst = [42, 42, 'foo', (1, 2, 3), 3.14159, 'foo']
x = set(lst)
print(x) # {3.14159, 42, (1, 2, 3), 'foo'}
集合操作: 包括用函数和运算符两种方式。
求并集:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1 | x2
{'baz', 'quux', 'qux', 'bar', 'foo'}
>>> x1.union(x2)
{'baz', 'quux', 'qux', 'bar', 'foo'}
求多个集合的并集:
>>> a = {1, 2, 3, 4}
>>> b = {2, 3, 4, 5}
>>> c = {3, 4, 5, 6}
>>> d = {4, 5, 6, 7}
>>> a.union(b, c, d)
{1, 2, 3, 4, 5, 6, 7}
>>> a | b | c | d
{1, 2, 3, 4, 5, 6, 7}
求交集:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.intersection(x2)
{'baz'}
>>> x1 & x2
{'baz'}
求多个集合的交集:
>>> a = {1, 2, 3, 4}
>>> b = {2, 3, 4, 5}
>>> c = {3, 4, 5, 6}
>>> d = {4, 5, 6, 7}
>>> a.intersection(b, c, d)
{4}
>>> a & b & c & d
{4}
求两个集合的差集:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.difference(x2)
{'foo', 'bar'}
>>> x1 - x2
{'foo', 'bar'}
从一个集合中减到多个集合后的差集:
>>> a = {1, 2, 3, 30, 300}
>>> b = {10, 20, 30, 40}
>>> c = {100, 200, 300, 400}
>>> a.difference(b, c)
{1, 2, 3}
>>> a - b - c
{1, 2, 3}
求两个集合的对称差集:相当于(x1-x2)union(x2-x1)。
x1.symmetric_difference(x2),但此函数只支持求两个集合的对称差集。 多个集合的对称差集可以使用"^"操作符连接。
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.symmetric_difference(x2)
{'foo', 'qux', 'quux', 'bar'}
>>> x1 ^ x2
{'foo', 'qux', 'quux', 'bar'}
下面演示用操作符"^"求多个集合的对称差集。
>>> a = {1, 2, 3, 4, 5}
>>> b = {10, 2, 3, 4, 50}
>>> c = {1, 50, 100}
>>> a ^ b ^ c
{100, 5, 10}
x1.isdisjoint(x2): 判断两个集合之间是否有交集。返回bool值。
>>> x1 = {1, 3, 5}
>>> x2 = {2, 4, 6}
>>> x1.isdisjoint(x2)
True
>>> x1 & x2
set()
判断一个集合是否为另一个集合的子集。函数:x1.issubset(x2),操作符:<=
>>> x1 = {'foo', 'bar', 'baz'}
>>> x1.issubset({'foo', 'bar', 'baz', 'qux', 'quux'})
True
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1 <= x2
False
一个集合是其自身的子集。
>>> x = {1, 2, 3, 4, 5}
>>> x.issubset(x)
True
>>> x <= x
True
x1 < x2这种运算符可以判断一个集合是否为另一个集合的真子集(proper subset),但这没有等价的函数。
>>> x1 = {'foo', 'bar'}
>>> x2 = {'foo', 'bar', 'baz'}
>>> x1 < x2
True
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'bar', 'baz'}
>>> x1 < x2
False
>>> x = {1, 2, 3, 4, 5}
>>> x <= x
True
>>> x < x
False
判断一个集合是否为另一个集合的超集: x1.issuperset(x2)等价于操作符x1 >= x2。
>>> x1 = {'foo', 'bar', 'baz'}
>>> x1.issuperset({'foo', 'bar'})
True
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1 >= x2
False
同样真超集(proper superset)只有x1 > x2,没有等价的函数。
集合的修改:前面看到的这些运算符和函数都是不改变原集合,而是产生新的集合。 虽然集合内的元素必须是不可变类型的,但集合本身却是可变类型的。
下面列举的名称中函数名中有"update"的函数,传入的参数即可以是集合,也可以是其它类型的可迭代对象,比如传入列表。
用并集来修改。
x1.update(x2[, x3 ...])
x1 = {'foo', 'bar', 'baz'}
x2 = {'foo', 'baz', 'qux'}
x1.update(x2)
print(x1) # {'qux', 'foo', 'baz', 'bar'}
等价的操作符是|=
x1 |= x2 [| x3 ...]
x1 = {'foo', 'bar', 'baz'}
x2 = {'foo', 'baz', 'qux'}
x1 |= x2
print(x1)
用交集来修改
x1.intersection_update(x2[, x3 ...])
x1 &= x2 [& x3 ...]
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>> x1 &= x2
>>> x1
{'foo', 'baz'}
>>> x1.intersection_update(['baz', 'qux'])
>>> x1
{'baz'}
用差集来修改集合
x1.difference_update(x2[, x3 ...])
x1 -= x2 [| x3 ...]
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>> x1 -= x2
>>> x1
{'bar'}
>>> x1.difference_update(['foo', 'bar', 'qux'])
>>> x1
set()
用对称差集来修改集合
x1.symmetric_difference_update(x2)
x1 ^= x2
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>>
>>> x1 ^= x2
>>> x1
{'bar', 'qux'}
>>>
>>> x1.symmetric_difference_update(['qux', 'corge'])
>>> x1
{'bar', 'corge'}
其他修改集合的方法如下:
x.add(
>>> x = {'foo', 'bar', 'baz'}
>>> x.add('qux')
>>> x
{'bar', 'baz', 'foo', 'qux'}
x.remove(
>>> x = {'foo', 'bar', 'baz'}
>>> x.remove('baz')
>>> x
{'bar', 'foo'}
>>> x.remove('qux')
Traceback (most recent call last):
File "<pyshell#58>", line 1, in <module>
x.remove('qux')
KeyError: 'qux'
x.discard(
>>> x = {'foo', 'bar', 'baz'}
>>> x.discard('baz')
>>> x
{'bar', 'foo'}
>>> x.discard('qux')
>>> x
{'bar', 'foo'}
x.pop(): 从集合中随机移除一个元素,此函数不接收任何参数。当集合为空时会报错。
>>> x = {'foo', 'bar', 'baz'}
>>> x.pop()
'bar'
>>> x
{'baz', 'foo'}
>>> x.pop()
'baz'
>>> x
{'foo'}
>>> x.pop()
'foo'
>>> x
set()
>>> x.pop()
Traceback (most recent call last):
File "<pyshell#82>", line 1, in <module>
x.pop()
KeyError: 'pop from an empty set'
x.clear(): 移除集合中所有元素。
>>> x = {'foo', 'bar', 'baz'}
>>> x
{'foo', 'bar', 'baz'}
>>>
>>> x.clear()
>>> x
set()
Frozen Sets: frozen set跟普通set相比,有一个特殊性,就是它是不可变类型。 意味着你不能使用add, update等这些函数去修改其内容。 但那些集合操作符仍然可以使用,比如 |=, &=, 但要注意,它们没有修改原来的集合,而是让变量指向了新的集合。
下面的示例中可以看到,集合操作符返回了新集合,id值不一样了。
>>> f = frozenset(['foo', 'bar', 'baz'])
>>> id(f)
56992872
>>> s = {'baz', 'qux', 'quux'}
>>> f &= s
>>> f
frozenset({'baz'})
>>> id(f)
56992152
即然frozen set是不可变类型,我们就可以将其用做dictionary的key,也可以用做普通集合的元素。
集合的性能
集合是无序的,非重复的,里面存储的内容必须是可hash的,即可以用hash()函数求哈希值的。一般不可变类型都是可哈希的。像tuple,string, number等。而列表,字典,集合本身等都是不可哈希的。
集合的创建是不如列表元组等效率高的,但集合中用remove函数移除一个值是高效的,时间复杂度是O(1)。原理是集合中每填加一个元素时,先对元素求哈希值,然后对应到集合内存中与哈希值相同(应该是可映射或很快能找到)的内存单元地址中。移除一个值时,也是先对值求hash,然后就可以快速定位到元素所在内存地址。 所以集合中的元素在内存中不是连续挨着存放的,中间很可能有很大的内存单元是空余的,因为每个元素的hash值是不可预期的。所以在集合中存储相同数量的元素时内存给其分配的一块存储空间要远大于为列表分配的内存空间。
用集合求可迭代对象的去重值是高效的,比我们自己手动实现这样的功能要快,因为是C底层优化过的。
集合中填加一个元素要比在列表头部增加元素高效,但应该近似于列表append的在尾部填加的效果。
成员检测 a in set 是高效的,就是直接计算其hash值,然后去检测对应的内存地址。

浙公网安备 33010602011771号