Python 程式設計入門 - 05

Pyladies Taiwan

Speaker : Mars

2017/11/19

Roadmap

  • 前情提要:字典
  • 再談字典
  • 集合
  • 集合的數學運算
  • 綜合練習:密碼學
  • 補充知識

前情提要:字典

字典

  • d[key]:字典以鍵值key去存取資料value,很像是串列的索引index
  • key需為不可變物件 (eg. intfloatstrtuple)
In [1]:
d = {"Mars":20,"姿君":25,"毛毛":10}
d["Mars"] += 15
d["姿君"] += 5
print(d)
print(d["Mars"],d["姿君"])
{'毛毛': 10, '姿君': 30, 'Mars': 35}
35 30

注意!

  • 字典是「非有序」的資料型別
  • 取用不存在的key,會出錯
In [2]:
d = {} # 空字典
d["Mars"] = 20
d["姿君"] = 25
d["毛毛"] = 10
print(d)
print(len(d))
{'毛毛': 10, '姿君': 25, 'Mars': 20}
3
In [3]:
print(d['MarsW'])
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-3-8ab309ac3ada> in <module>()
----> 1 print(d['MarsW'])

KeyError: 'MarsW'

[練習] 計算庫存

水果庫存、價格都以字典型別儲存,
請計算若水果都賣出後,可賺進多少錢?

In [4]:
stock={
    "banana":5,
    "apple":3,
    "orange":10
}
price={
    "banana":5,
    "apple":20,
    "orange":15
}
In [5]:
revenue = 0
revenue += stock["banana"] * price["banana"]
revenue += stock["apple"] * price["apple"]
revenue += stock["orange"] * price["orange"]
print(revenue)
235

字典的迭代

字典以鍵值key去存取資料value

可迭代者 Iterable、迭代器 Iterator

In [6]:
revenue = 0
for fruit in stock:
    print(fruit,stock[fruit],price[fruit])
    revenue += stock[fruit] * price[fruit]
print(revenue)
apple 3 20
orange 10 15
banana 5 5
235

再談字典

字典的鍵值是唯一的

In [7]:
d = {} # 空字典
d["Mars"] = 20
d["姿君"] = 25
d["毛毛"] = 10
print(d)
d["Mars"] = 30
print(d)
{'毛毛': 10, '姿君': 25, 'Mars': 20}
{'毛毛': 10, '姿君': 25, 'Mars': 30}

並不會保留本來「Mars:20」,然後再新增一個「Mars:30」
而是直接把Mars這個key指向30這個value

字典是可變物件

In [8]:
d = {} # 空字典
print(id(d))
d["Mars"] = 20
d["姿君"] = 25
d["毛毛"] = 10
print(id(d))
140553010539976
140553010539976

取得所有鍵值 d.keys()

In [9]:
stock={
    "banana":5,
    "apple":3,
    "orange":10
}
print(stock.keys())
for i in stock.keys():
    print(i)
dict_keys(['apple', 'orange', 'banana'])
apple
orange
banana

取得所有值 d.values()

In [10]:
stock={
    "banana":5,
    "apple":3,
    "orange":10
}
print(stock.values())
for i in stock.values():
    print(i)
dict_values([3, 10, 5])
3
10
5

取得所有鍵值與值的配對 d.items()

In [11]:
stock={
    "banana":5,
    "apple":3,
    "orange":10
}
print(stock.items())
for i in stock.items():
    print(i)
dict_items([('apple', 3), ('orange', 10), ('banana', 5)])
('apple', 3)
('orange', 10)
('banana', 5)

集合 Set

集合跟字典一樣也是

  • 「非有序」的資料型別
  • 可變物件
  • 元素是唯一的

建立-以大括號{}放入元素

  • 為空的話會是字典
In [12]:
st = {1, 3.5, 'b', 2, 'a', 6, 'a', 2}
print(type(st))
print(st)
d = {}
print(type(d))
<class 'set'>
{1, 2, 3.5, 6, 'b', 'a'}
<class 'dict'>

建立-以內建函式set(s)

  • 為空即建立空集合
  • s 為可迭代者
In [13]:
st = set()
print(type(st))
<class 'set'>
In [14]:
st = set("PyLadies")
print(st)
{'d', 's', 'y', 'a', 'e', 'P', 'L', 'i'}
In [15]:
st = set([1, 3.5, 'b', 2, 'a', 6])
print(st)
{1, 2, 3.5, 6, 'b', 'a'}
In [16]:
st = set(range(5))
print(st)
{0, 1, 2, 3, 4}
In [17]:
st = set({"banana":6,"apple":3})
print(st)
{'apple', 'banana'}

注意!

  • 元素只能是不可變物件
In [18]:
st = {1, 3.5, 'b', 2, 'a', [1,2]}
print(st)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-18-792f1266cfed> in <module>()
----> 1 st = {1, 3.5, 'b', 2, 'a', [1,2]}
      2 print(st)

TypeError: unhashable type: 'list'
In [19]:
st = set([1, 3.5, 'b', 2, 'a', [1,2]])
print(st)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-19-61479bafb898> in <module>()
----> 1 st = set([1, 3.5, 'b', 2, 'a', [1,2]])
      2 print(st)

TypeError: unhashable type: 'list'

[練習] 將文章資料轉成集合

  • 請問以下出現哪些不重複國家?

答案(順序可能會不一)

{'France', 'New Zealand', 'Taiwan', 'UK', 'USA'}

s = """
UK
Taiwan
USA
France
New Zealand
UK
France 
"""

Hint:

In [20]:
s = """
UK
Taiwan
USA
France
New Zealand
UK
France 
"""
print(s.split('\n'))
# 想想看strip()要放在split前面還是後面
['', 'UK', 'Taiwan', 'USA', 'France', 'New Zealand', 'UK', 'France ', '']

增加-集合.add(不可變物件)

In [21]:
st = set()
print(id(st))
st.add(1)
st.add('a')
st.add(3.5)
st.add(2)
print(st)
print(id(st))
140553093892808
{1, 'a', 3.5, 2}
140553093892808

[練習] 資料會是分別輸入,而不是一次給足的方式

  • 我會輸入7次國家
  • 產生不重複的國家清單
    UK
    Taiwan
    USA
    France
    New Zealand
    UK
    France

Hint:

  • 先宣告一個空集合,等下隨著每次輸入增加
  • 迴圈配合range()
  • 輸入的函式為 input(),輸入結果為字串
st = set()
for i in range(__):
    keyin = ______
    st.___(keyin)
print(st)

複製

集合.copy()

  • Shallow Copy 淺複製
In [22]:
st1 = {1, 2, 'a', 3.5}
st2 = st1.copy()
print("st1",st1)
print("st2",st2)
st1 {'a', 2, 3.5, 1}
st2 {'a', 2, 3.5, 1}

刪除

集合.clear()

  • 清除所有元素
In [23]:
st = {1, 2, 'a', 3.5}
st.clear()
print(st)
set()

集合.pop()

  • 隨機拿出某元素,並從原本的集合移除
  • 如果是空集合,會報錯
In [24]:
st = {1, 2, 'a'}
print(st.pop())
print(st)
print(st.pop())
print(st)
print(st.pop())
print(st)
print(st.pop())
a
{2, 1}
2
{1}
1
set()
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-24-fa3b9b14188d> in <module>()
      6 print(st.pop())
      7 print(st)
----> 8 print(st.pop())

KeyError: 'pop from an empty set'

集合.remove(元素)

  • 從集合中移除元素
  • 若元素不存在,會報錯
In [25]:
st = {1, 2, 'a'}
st.remove(2)
print(st)
st.remove('b')
{'a', 1}
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-25-3ac930768714> in <module>()
      2 st.remove(2)
      3 print(st)
----> 4 st.remove('b')

KeyError: 'b'

集合.discard(元素)

  • 從集合中移除元素
  • 會檢查元素存在集合與否,若元素不存在,就不做事
In [26]:
st = {1, 2, 'a'}
st.discard(2)
print(st)
st.discard('b')
print(st)
{'a', 1}
{'a', 1}

集合的數學運算

長度len(集合)

  • 有幾個元素
In [27]:
st = {1, 2, 'a'}
print(len(st))
3

[練習] 資料會是分別輸入,而不是一次給足的方式

  • 第一行輸入我將會輸入少資料
  • 告訴我有幾個不同國家(以下範例答案是5)
    7
    UK
    Taiwan
    USA
    France
    New Zealand
    UK
    France

HackerRank - Set .add()

Hint:

  • input(),輸入結果為字串
num = input()
st = set()
for i in range(____):
    keyin = input()
    st.add(keyin)
print(____)

元素是否存在

  • 元素 in 集合
  • 元素 not in 集合
In [28]:
st = {1, 2, 'a'}
print('b' in st)
print('b' not in st)
False
True

子集合 subset

  • 子集合<=父集合
  • 子集合.issubset(父集合)
  • 相等也是True
In [29]:
st0 = {1,2,'a'}
st1 = {'a'}
st2 = {3,'a'}
st3 = {1,2,'a'}
print('st1<=st0         ',st1<=st0)
print('st2.issubset(st0)',st2.issubset(st0))
print('st3.issubset(st0)',st3.issubset(st0))
st1<=st0          True
st2.issubset(st0) False
st3.issubset(st0) True

真子集合 proper subset

  • 子集合<父集合
  • 子集合.issubset(父集合) and 子集合!=父集合
  • 相等是False
In [30]:
st0 = {1,2,'a'}
st1 = {'a'}
st2 = {3,'a'}
st3 = {1,2,'a'}
print('st1<st0   ',st1<st0)
print('st2<(st0) ',st2<(st0))
print('st3<(st0) ',st3<(st0))
print('st3<=(st0)',st3<=(st0))
st1<st0    True
st2<(st0)  False
st3<(st0)  False
st3<=(st0) True

父集合 superset

  • 父集合>=子集合
  • 父集合.issuperset(子集合)
  • 相等也是True
In [31]:
st0 = {2,'a'}
st1 = {1,3,'a'}
st2 = {1,2,'a'}
st3 = {2,'a'}
print('st1>=st0           ',st1>=st0)
print('st1.issuperset(st0)',st1.issuperset(st0))
print('st2.issuperset(st0)',st2.issuperset(st0))
print('st3.issuperset(st0)',st3.issuperset(st0))
st1>=st0            False
st1.issuperset(st0) False
st2.issuperset(st0) True
st3.issuperset(st0) True

超父集合 proper superset

  • 父集合>子集合
  • 父集合.issuperset(子集合) and 子集合!=父集合
  • 相等是False
In [32]:
st0 = {2,'a'}
st1 = {1,3,'a'}
st2 = {1,2,'a'}
st3 = {2,'a'}
print('st1>st0 ',st1>st0)
print('st2>st0 ',st2>st0)
print('st3>st0 ',st3>st0)
print('st3>=st0',st3>st0)
st1>st0  False
st2>st0  True
st3>st0  False
st3>=st0 False

無交集(交集為空)

  • 集合1.isdisjoint(集合2)
In [33]:
st0 = {1,2,3,4,5}
st1 = {1,3,7}
st2 = {6,7,8}
print(st0.isdisjoint(st1))
print(st0.isdisjoint(st2))
False
True

聯集:集合間相加,去除重複元素(產生新集合)

  • 集合1 | 集合2 | ...
  • 集合1.union(集合2,...)
  • In [34]:
    st0 = {1,2,3,4,5}
    st1 = {1,3,7}
    st2 = {6,7,8}
    print(st0 | st1)
    print(st0.union(st1))
    print(st0 | st1 | st2)
    print(st0.union(st1,st2))
    print('st0 =',st0)
    
    {1, 2, 3, 4, 5, 7}
    {1, 2, 3, 4, 5, 7}
    {1, 2, 3, 4, 5, 6, 7, 8}
    {1, 2, 3, 4, 5, 6, 7, 8}
    st0 = {1, 2, 3, 4, 5}
    

    方法可以接受可迭代者,但運算子不行

    In [35]:
    st0 = {1,2,3,4,5}
    st1 = [1,3,7]
    print(st0.union(st1))
    print(st0 | st1)
    
    {1, 2, 3, 4, 5, 7}
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-35-67f2a8e5e38f> in <module>()
          2 st1 = [1,3,7]
          3 print(st0.union(st1))
    ----> 4 print(st0 | st1)
    
    TypeError: unsupported operand type(s) for |: 'set' and 'list'

    聯集並更新(原地修改)

    • 集合1 |= 集合2 | ...
    • 集合1.update(集合2,...)
    In [36]:
    st0 = {1,2,3,4,5}
    st1 = {1,3,7}
    st2 = {6,7,8}
    st3 = {5,9}
    st0 |= st1 | st2
    print(id(st0),st0)
    st0.update(st3)
    print(id(st0),st0)
    
    140553010225448 {1, 2, 3, 4, 5, 6, 7, 8}
    140553010225448 {1, 2, 3, 4, 5, 6, 7, 8, 9}
    

    交集:集合間重複的元素(產生新集合)

  • 集合1 & 集合2 & ...
  • 集合1.intersection(集合2,...)
  • In [37]:
    st0 = {1,2,3,4,5}
    st1 = {1,3,7}
    st2 = {6,7,8}
    print(st0 & st1)
    print(st0.intersection(st1))
    print(st0 & st1 & st2)
    print(st0.intersection(st1,st2))
    print('st0 =',st0)
    
    {1, 3}
    {1, 3}
    set()
    set()
    st0 = {1, 2, 3, 4, 5}
    

    交集並更新(原地修改)

    • 集合1 &= 集合2 & ...
    • 集合1.intersection_update(集合2,...)
    In [38]:
    st0 = {1,2,3,4,5}
    st1 = {1,3,7}
    st2 = {3,6,9}
    st0 &= st1 & st2
    print(st0)
    
    {3}
    

    差集:集合間不重複的元素(產生新集合)

    • 集合1 - 集合2 - ...
    • 集合1.difference(集合2,...)
    In [39]:
    st0 = {1,2,3,4,5}
    st1 = {1,3,7}
    st2 = {2,4,6,8}
    print(st0 - st1)
    print(st0.difference(st1))
    print(st0 - st1 - st2)
    print(st0.difference(st1,st2))
    print('st0 =',st0)
    
    {2, 4, 5}
    {2, 4, 5}
    {5}
    {5}
    st0 = {1, 2, 3, 4, 5}
    

    差集並更新(原地修改)

    • 集合1 -= 集合2 | ...
    • 集合1.difference_update(集合2,...)
    In [40]:
    st0 = {1,2,3,4,5}
    st1 = {1,3,7}
    st2 = {2,4,6,8}
    st0 -= st1 | st2
    print(st0)
    
    {5}
    

    對稱差集:兩集合相加,去除兩集合重複的元素(產生新集合)

  • 集合1 ^ 集合2
  • 集合1.symmetric_difference(集合2)
  • In [41]:
    st0 = {1,2,3,4,5}
    st1 = {1,3,7}
    print(st0 ^ st1)
    print(st0.symmetric_difference(st1))
    print('st0 =',st0)
    
    {2, 4, 5, 7}
    {2, 4, 5, 7}
    st0 = {1, 2, 3, 4, 5}
    

    對稱差集並更新(原地修改)

    • 集合1 ^= 集合2
    • 集合1.symmetric_difference_update(集合2)
    In [42]:
    st0 = {1,2,3,4,5}
    st1 = {1,3,7}
    st0 ^= st1
    print('st0 =',st0)
    
    st0 = {2, 4, 5, 7}
    

    [練習] 統計

    以下是PyLadies參加入門01、02活動的名單,可以告訴我們:

    • 01、02分別有多少人參加?
    • 01、02不重複的參加的人數共有多少?(參加過01也參加過02,只算一次)
    • 有哪些人01、02都有參加的?總共人數為?
    • 哪些是參加02但沒參加過01的人?總共人數為?
    s1 = {'Cathy','Chloe Hsu','Claire Yu Shan OU','Croc East','Dana Tai','Dobe','Ella Cheng','EmilyChen TW','Eunice','F. Wu','Fion Chen','GloriaLee','Heidi Chieh-An Lin','Huilan','Jamie Yeh','Jessie Chou','Jovianne Yen Hsun Lin','Joy Chou','joyjhiang','Kate Lizzie','Kelly','Ling Huang','Mandy','MarsW','Mei-Yao Cheng','Michelle Y. Chou','Miko Liu Yiyu','Mini Hu','Nata','Pei Lee','pipi Lan','rose350051','Ruby Lu','sandy c','Sara','Sharon Lin','Sonia','Stella.Chang','Szu-ying Lee','Tao','Tina Han','Tracy','Vicky Tsai','Weishan','Xiang Ying Yao','YICHUN','YuLu Tung','Yuting Hung','Yvonne Huang','乃勤','卓家璘','姿君 (Kelly)','張耀文','林心誼','王翊帆','葉靜縈','譚余家','陳冠穎','陳品君','陳娜娜','陳宣瑀','黃媺芝'}
    
    s2 = {'Angela Chang','Anna Yen','Annz Xue','Chloe Hsu','Croc East','Dana Tai','Dobe','Ella Cheng','Eugenia','F. Wu','Fion Chen','gabrielzzy','Heidi Chieh-An Lin','Irene Yu-hsuan Chen','Jovianne Yen Hsun Lin','Kelly','Kelly Hung','Mandy','MarsW','Michelle Y. Chou','Mini Hu','Pei Lee','Plurence','Po-Ying Fu','rose350051','Ruby Lu','Sonia','Tina Han','Tracy','Vicky','Weishan','Xiang Ying Yao','YICHUN','YuLu Tung','Yuting Hung','Yvonne Huang','乃勤','卓家璘','姿君 (Kelly)','張家瑜','林心誼','王翊帆','葉靜縈','陳冠穎','陳品君'}
    

    Hint:

    • 01、02分別有多少人參加?
      • 集合裡面有多少個元素,可以用 len()
    • 01、02不重複的參加的人數共有多少?(參加過01也參加過02,只算一次)
      • 不重複的參加人數,就是01+02但扣除重複的,也就是聯集
    • 有哪些人01、02都有參加的?總共人數為?
      • 交集
    • 哪些是參加02但沒參加過01的人?總共人數為?
      • 差集,或是用參加02的人數扣掉「參加02也參加過01」的人

    應用情境:

    補充知識-不可變的集合

    因為集合set是可變型別,不能做字典的鍵值,也不能做集合的元素,
    但Python另外還提供一個叫做frozenset的集合型別,是不可變而且可雜湊。

    In [43]:
    st1 = {6,7,8}
    st1.add(frozenset([1,2,3]))
    print(st1)
    st1.add(set([1,2,3]))
    print(st1)
    
    {8, frozenset({1, 2, 3}), 6, 7}
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-43-5769aed7f2e1> in <module>()
          2 st1.add(frozenset([1,2,3]))
          3 print(st1)
    ----> 4 st1.add(set([1,2,3]))
          5 print(st1)
    
    TypeError: unhashable type: 'set'

    注意!

    • 原地修改的運算子,是改指向新的物件
    • 不支援能修改的方法
    • 其餘操作皆和set相同
    In [44]:
    fs = frozenset([1,2,3])
    st = {1,3,5}
    print(fs&st)
    print(id(fs),fs)
    fs &= st
    print(id(fs),fs)
    
    frozenset({1, 3})
    140553010242504 frozenset({1, 2, 3})
    140553093892584 frozenset({1, 3})
    
    In [45]:
    fs = frozenset([1,2,3])
    fs.add(6)
    
    ---------------------------------------------------------------------------
    AttributeError                            Traceback (most recent call last)
    <ipython-input-45-8ff2ceb2b608> in <module>()
          1 fs = frozenset([1,2,3])
    ----> 2 fs.add(6)
    
    AttributeError: 'frozenset' object has no attribute 'add'

    補充知識-字典與集合生成式

    在短短一行程式碼就能產生想要資料內容

    集合

    串列生成式類似

    • 運算式 for 名稱 in 可迭代者
    • 運算式 for 名稱 in 可迭代者 if 運算式

    字典

    • 運算式:運算式 for 名稱,名稱 in 可迭代者
    • 運算式:運算式 for 名稱,名稱 in 可迭代者 if 運算式

    過濾出不及格的學生-集合

    In [46]:
    st = {32,56,58,62,79,82,98}
    st1=set()
    for i in st:
        if i<60:
            st1.add(i)
    print(st1)
    
    {32, 56, 58}
    
    In [47]:
    st = {32,56,58,62,79,82,98}
    st1 = {x for x in st if x<60}
    print(st1)
    
    {32, 56, 58}
    

    過濾出不及格的學生-字典

    In [48]:
    d = {'a':32, 'b':56, 'c':58, 'd':62, 'e':79, 'f':82, 'g':98} 
    d1= {}
    for key in d:
        if d[key]<60:
            d1[key]=d[key]
    print(d1)
    
    {'a': 32, 'c': 58, 'b': 56}
    
    In [49]:
    d = {'a':32, 'b':56, 'c':58, 'd':62, 'e':79, 'f':82, 'g':98} 
    d1= {key:value for key,value in d.items() if value<60}
    print(d1)
    
    # key,value翻轉
    d2= {value:key for key,value in d.items() if value<60}
    print(d2)
    
    {'a': 32, 'c': 58, 'b': 56}
    {32: 'a', 56: 'b', 58: 'c'}
    

    串列轉字典

    enumerate
    chr()ord()

    In [50]:
    l = [32,56,58,62,79,82,98]
    d = {}
    for index,value in enumerate(l,ord("a")):
        print(index,chr(index),value)
        d[chr(index)]=value
    print(d)
    
    97 a 32
    98 b 56
    99 c 58
    100 d 62
    101 e 79
    102 f 82
    103 g 98
    {'a': 32, 'd': 62, 'e': 79, 'f': 82, 'g': 98, 'c': 58, 'b': 56}
    
    In [51]:
    l = [32,56,58,62,79,82,98]
    d = {chr(key):value for key,value in enumerate(l,ord("a"))}
    print(d)
    
    {'a': 32, 'd': 62, 'e': 79, 'f': 82, 'g': 98, 'c': 58, 'b': 56}
    

    補充知識-可雜湊者Hashable

    字典的鍵、集合的元素前面都說是不可變物件,
    但正確說法是「可雜湊者」。

    Python內建的不可變物件是可雜湊者,
    可以透過雜湊演算法-內建函式hash(),得到獨一無二的雜湊值,
    不可變物件在存活期間,內容絕對不變,雜湊值也是獨一無二,也不會改變。
    而相對的可變物件則沒有此特性, 因此嚴格說起來不可變!=可雜湊(可以自行定義型別)

    • hash()會因操作環境不同而得到不同的結果
    d = {'apple':15, 'banana':30.5, 'orange':"$48"}
    print(hash('apple'))
    print(hash('banana'))
    print(hash('orange'))
    print(hash('apple'))
    

    -6616432743583863425
    -5363598976648845331
    8163426422985150588
    -6616432743583863425

    s = {15, 30.5, "$48"}
    print(hash(15))
    print(hash(30.5))
    print(hash("$48"))
    

    15
    1152921504606847006
    8422802888266984843

    所以其實字典、集合的示意圖是像以下這樣:

    學習資源對應