1. 程式人生 > >Python序列結構--集合

Python序列結構--集合

tab 適用於 get 基本類 erro cal card 多個 代碼

集合:元素之間不允許重復

  • 集合屬於Python無序可變序列,元素之間不允許重復

集合對象的創建與刪除

  • 直接將值賦值給變量即可創建一個集合

    >>> a = {3,5}
    >>> type(a)
    <class ‘set‘>
  • set()函數將列表、元組、字符串、range對象等其他可叠代對象轉換為集合,如果原來的數據中存在重復元素,則轉換為集合的時候只保留一個;如果原序列或叠代對象中有不可哈希的值,無法轉換為集合,拋出異常

    >>> a_set=set(range(8,14))
    >>> a_set
    {8, 9, 10, 11, 12, 13}
    >>> b_set = set([0,1,2,3,0,1,2,3,7,8])
    >>> b_set
    {0, 1, 2, 3, 7, 8}
  • 集合推導式來快速生成集合

    >>> {x.strip() for x in (‘he ‘,‘she  ‘,‘  I‘)}
    {‘I‘, ‘he‘, ‘she‘}
    >>> import random
    >>> x = {random.randint(1,500) for i in range(100)} #生成隨機數,自動去除重復的元素
    >>> len(x) # 一般而言輸出結果會小於100
    94
    >>> {str(x) for x in range(10)}
    {‘1‘, ‘9‘, ‘4‘, ‘8‘, ‘3‘, ‘7‘, ‘2‘, ‘6‘, ‘0‘, ‘5‘}

集合的操作與運算

集合元素的增加和刪除
  • add()方法可以增加新元素,如果該元素已存在則忽略該操作,不會拋出異常;update()方法合並另外一個集合中的元素到當前集合中,並且自動去除重復元素

    >>> s = {1,2,3}
    >>> s.add(3) # 添加元素,重復元素自動忽略
    >>> s.update({3,4}) # 更新字典,自動忽略重復的元素
    >>> s
    {1, 2, 3, 4}
  • pop()方法隨機刪除並返回集合中的一個元素,如果為空則拋出異常;remove()方法刪除集合中的元素,如果指定元素不存在則拋出異常;discard()方法從集合中刪除一個特定的元素,如果元素不存在則忽略該操作;clear()方法清空集合

    >>> s
    {1, 2, 3, 4}
    >>> s.discard(5) # 刪除元素,不存在則忽略該操作
    >>> s.remove(5) # 刪除元素,不存在則拋出異常
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    KeyError: 5
    >>> s.pop() # 刪除並返回元素
    1
    >>> s
    {2, 3, 4}
    >>> s.clear()
    >>> s
    set()
集合運算
  • 內置函數len()、max()、min()、sum()、sorted()、map()、filter()、enumerate()等也適用於集合。另外還支持數學意義上的交集、並集、差集等運算

    >>> a_set = set([8,9,10,11,12,113])
    >>> a_set
    {8, 9, 10, 11, 12, 113}
    >>> b_set = {0,1,2,3,7,8}
    >>> a_set | b_set # 並集
    {0, 1, 2, 3, 7, 8, 9, 10, 11, 12, 113}
    >>> a_set.union(b_set) # 並集
    {0, 1, 2, 3, 7, 8, 9, 10, 11, 12, 113}
    >>> a_set & b_set # 交集
    {8}
    >>> a_set.intersection(b_set)
    {8}
    >>> a_set.difference(b_set) # 差集
    {9, 10, 11, 12, 113}
    >>> b_set.difference(a_set)
    {0, 1, 2, 3, 7}
    >>> a_set - b_set
    {9, 10, 11, 12, 113}
    >>> b_set - a_set
    {0, 1, 2, 3, 7}
    >>> a_set.symmetric_difference(b_set) # 對稱差集
    {0, 1, 2, 3, 7, 9, 10, 11, 12, 113}
    >>> a_set ^ b_set
    {0, 1, 2, 3, 7, 9, 10, 11, 12, 113}
    >>> x = {1,2,3}
    >>> y = {1,2,5}
    >>> z = {1,2,3,4}
    >>> x < y # 比較集合大小/包含關系
    False
    >>> x < z # 真子集True>>> y < zFalse>>> z < zFalse>>> z <= z # 子集True>>> x.issubset(y) # 測試是否為子集False>>> x.issubset(z)True>>> x.issubset(x)True

不可變集合frozenset

用法與set基本類似,與set類不同的是,frozenset是不可變集合,沒有提供add()、remove()等可以修改集合對象的方法
>>> x = frozenset(range(5))
>>> x
frozenset({0, 1, 2, 3, 4})
>>> x.add(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: ‘frozenset‘ object has no attribute ‘add‘
>>> x | frozenset(range(5,10)) # 並集
frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
>>> x & frozenset(range(4,10)) # 交集
frozenset({4})
>>> x - frozenset(range(4,10)) # 差集
frozenset({0, 1, 2, 3})
>>> frozenset(range(4)) < frozenset(range(5)) # 集合包含關系比較
True

集合應用案例

  • python字典和集合都使用hash表來存儲元素,元素查詢速度快,關鍵字in作用於字典和集合比作用於列表要快得多

    >>> import random
    >>> x1 = list(range(10000))
    >>> x2 = tuple(range(10000))
    >>> x3 = set(range(10000))
    >>> x4 = dict(zip(range(10000),range(10000)))
    >>> r = random.randint(0,9999)
    >>> for t in (x4, x3, x2, x1):
    ... start = time.time()
    ... for i in range(9999999):
    ... flag = (r in t)
    ... print(type(t),‘time used:‘, time.time() - start)
    ...
    <class ‘dict‘> time used: 0.865715503692627
    <class ‘set‘> time used: 0.9040701389312744
    <class ‘tuple‘> time used: 487.52976393699646
    <class ‘list‘> time used: 488.0697581768036
  • 作為集合快速的具體應用,可以使用集合快速提取序列中單一元素,即提取序列中所有不重復的元素

    >>> import random
    >>> listRandom = [random.choice(range(1000)) for i in range(100)]
    >>> len(listRandom)
    100
    >>> newSet = set(listRandom)
    >>> len(newSet)
    95
  • 返回指定範圍內一定數量的不重復數字

    >>> import random
    >>> def randomNumbers(number,start,end):
    ... ‘‘‘使用集合生成number個介於start和end之間的不重復隨機數‘‘‘
    ... data = set()
    ... while len(data) < number:
    ... element = random.randint(start,end)
    ... data.add(element)
    ... return data
    ...
    >>> randomNumbers(10,1,1000)
    {66, 676, 550, 522, 333, 783, 499, 278, 59, 349}
  • 返回指定範圍內一定數量的不重復數字,使用random模塊的sample()函數更好一些,但是random模塊的sample()函數只支持列表、元組、集合、字符串和range對象,不支持字典以及map、zip、enumerate、filter等惰性求值的叠代對象

    >>> import random
    >>> random.sample(range(1, 700), 10) # 選取指定分布中選取不重復元素
    [340, 489, 623, 121, 550, 632, 19, 531, 626, 591]
  • 下面兩段代碼用來測試指定列表中是否包含非法數據,很明顯第二段用集合的代碼效率高一些

    >>> import random
    >>> lstColor = (‘red‘,‘green‘,‘blue‘)
    >>> colors = [random.choice(lstColor) for i in range(10000)]
    >>> for item in colors:
    ... if item not in lstColor:
    ... print(‘error:‘,item)
    ... break
    ...
    >>>
    >>> if (set(colors) - set(lstColor)):
    ... print(‘error‘)
    ...
    >>>
  • 使用字典和集合模擬有向圖結構,並實現了節點的入度和出度計算

    >>> def getDegress(orientedGraph,node):
    ... outDegree = len(orientedGraph.get(node,[]))
    ... inDegree = sum(1 for v in orientedGraph.values() if node in v)
    ... return (inDegree, outDegree)
    ...
    >>> graph = {‘a‘ : set(‘bcdef‘), ‘b‘:set(‘ce‘), ‘c‘:set(‘d‘), ‘d‘: set(‘e‘), ‘e‘:set(‘f‘), ‘f‘:set(‘cgh‘),‘g‘:set(‘fhi‘), ‘h‘:set(‘fgi‘), ‘i‘:set() }
    >>> print(getDegress(graph, ‘h‘))
    (2, 3)
    >>>

序列解包的多種形式和用法

序列解包是Python中非常重要和常用的一個功能,可以使用非常簡潔的形式完成復雜的功能,提高代碼的可讀性,減少了程序員代碼的輸入量
>>> x,y,z = 1,2,3
>>> x
1
>>> y
2
>>> z
3
>>> v_tuple = (False,3.5,‘exp‘)
>>> (x,y,z) = v_tuple
>>> m,n,q = v_tuple
>>> m,n,q = range(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)
>>> m,n,q = range(3) # 對range對象進行序列解包
>>> x,y,z = range(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)
>>> x,y,z = iter([1,2,3]) # 使用叠代器對象進行序列解包
>>> x,y,z = map(str,range(3)) # 使用可叠代的map對象進行序列解包
>>> x
‘0‘
>>> y
‘1‘
>>> x,y =y,x # 交換兩個變量的值
>>> x
‘1‘
>>> y
‘0‘
序列解包還可用於列表、字典、enumerate對象、filter對象等,對字典使用時,默認是對字典“鍵”進行操作,如果對“鍵:值“對進行操作應使用字典的items()方法說明,如果需要對字典”值“進行操作應該使用字典的values()方法明確指定
>>> a = [1,2,3]
>>> b,c,d = a
>>> x,y,z = sorted([1,3,2])
>>> s = {‘a‘:1,‘b‘:2,‘c‘:3}
>>> b,c,d = s.items()
>>> b
(‘a‘, 1)
>>> c
(‘b‘, 2)
>>> d
(‘c‘, 3)
>>> b,c,d = s
>>> b
‘a‘
>>> c
‘b‘
>>> d
‘c‘
>>> b,c,d = s.values()
>>> b
1
>>> c
2
>>> d
3
>>> a,b,c=‘ABC‘
>>> a
‘A‘
>>> b
‘B‘
>>> c
‘C‘
使用序列解包可以很方便地同時遍歷多個序列
>>> keys = [‘a‘,‘b‘,‘c‘,‘d‘]
>>> values = [1,2,3,4]
>>> for k,v in zip(keys,values):
... print((k,v),end=‘ ‘)
...
(‘a‘, 1) (‘b‘, 2) (‘c‘, 3) (‘d‘, 4) >>>
>>> x = [‘a‘,‘b‘,‘c‘]
>>> for i,v in enumerate(x):
... print(‘The value on position {0} is {1}‘.format(i,v))
...
The value on position 0 is a
The value on position 1 is b
The value on position 2 is c
>>> s = {‘a‘:1,‘b‘:2,‘c‘:3}
>>> for k,v in s.items():
... print((k,v),end=‘ ‘)
...
(‘a‘, 1) (‘b‘, 2) (‘c‘, 3) >>>
序列解包的另類用法和錯誤的用法:
>>> print(*[1,2,3])
1 2 3
>>> print(*[1,2,3],4,*(5,6))
1 2 3 4 5 6
>>> *range(4),4
(0, 1, 2, 3, 4)
>>> *range(4)
File "<stdin>", line 1
SyntaxError: can‘t use starred expression here
>>> {*range(4),4,*(5,6,7)}
{0, 1, 2, 3, 4, 5, 6, 7}
>>> {‘x‘:1,**{‘y‘:2}}
{‘x‘: 1, ‘y‘: 2}
>>> a,b,c,d = range(3),3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 4, got 2)
>>> a,b,c,d = *range(3),3
下面的代碼看起來與序列解包類型,但嚴格來說是序列解包的逆運算,與函數的可變長度參數一樣,用來接收等號右側的多個數值
>>> a,*b,c = 1,2,3,4,5
>>> a
1
>>> b
[2, 3, 4]
>>> c
5
>>> a,b,c
(1, [2, 3, 4], 5)
>>> a,*b,c = range(20)
>>> b
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
>>> *b = 1,2,4 # 等號左邊必須為列表、元組或多個變量
File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple

Python序列結構--集合