Python 程式設計入門 - 04

Pyladies Taiwan

Speaker : Mars

2017/10/29

Roadmap

  • 前情提要:迴圈
  • 應用情境:密碼學
  • 常用的資料型別
  • 字典
  • 再談字典的處理
  • 補充知識

前情提要:迴圈

03 - 迴圈與迭代

應用情境:密碼學

03 - 應用情境:密碼學

常用的資料型別

應用情境:會員儲值

  • Mars剛開始有$20、姿君有$25、毛毛有$10
  • Mars儲值$15、姿君儲值$5
In [1]:
# 有幾個人就用幾個物件儲存
money_Mars = 20
money_姿君 = 25
money_毛毛 = 10

money_Mars+=15
money_姿君+=5

print("Mars",money_Mars)
print("姿君",money_姿君)
print("毛毛",money_毛毛)
Mars 35
姿君 30
毛毛 10
In [2]:
l = [["Mars",20],["姿君",25],["毛毛",10]]
# Mars 儲值 15元, 再來是 姿君儲值 5元
l[0][1]+=15
l[1][1]+=5
print(l)
[['Mars', 35], ['姿君', 30], ['毛毛', 10]]
In [3]:
# 比較有彈性
l = [["姿君",25],["Mars",20],["毛毛",10]]
for member in l:
    if member[0]=="Mars":
        member[1]+=15
for member in l:
    if member[0]=="姿君":
        member[1]+=5
print(l)
[['姿君', 30], ['Mars', 35], ['毛毛', 10]]

字典

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

注意!

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

KeyError: 'MarsW'

[練習] 計算庫存

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

stock={
    "banana":5,
    "apple":3,
    "orange":10
}
price={
    "banana":5,
    "apple":20,
    "orange":15
}

Hint:

revenue = 0
revenue += stock["____"] * price["____"]
revenue += stock["____"] * price["____"]
revenue += stock["____"] * price["____"]
print(revenue)

字典的迭代

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

可迭代者 Iterable、迭代器 Iterator

In [7]:
d = {"Mars":20,"姿君":25,"毛毛":10}
itb=iter(d)
print(next(itb))
print(next(itb))
print(next(itb))
姿君
毛毛
Mars
In [8]:
d = {"Mars":20,"姿君":25,"毛毛":10}
for key in d:
    print(key,d[key])
姿君 25
毛毛 10
Mars 20

[練習] 計算庫存-以迴圈處理

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

stock={
    "banana":5,
    "apple":3,
    "orange":10
}
price={
    "banana":5,
    "apple":20,
    "orange":15
}

Hint:

revenue = 0
for fruit in stock:
    revenue += stock[____] * price[____]
print(revenue)

如何把串列變成字典

In [9]:
l = [["姿君",25],["Mars",20],["毛毛",10]]
d = {}
for item in l:
    name = item[0]
    score = item[1]
    d[name]=score
print(d)
{'姿君': 25, '毛毛': 10, 'Mars': 20}
In [10]:
l = [["姿君",25],["Mars",20],["毛毛",10]]
d = {}
for name,score in l:
    d[name]=score
print(d)
{'姿君': 25, '毛毛': 10, 'Mars': 20}

[練習] 遊戲計分

遊戲中計分,第一回合:A得10分、第二回合B得5分...,最後要加總算分數。

l = [["A",10],["B",5],["C",23],["B",7],["A",6],["A",3],["C",2]]

答案是:

C的得分25
B的得分12
A的得分19

Hint:

  • 可以參考剛才提到的「如何把串列變成字典」
l = [["A",10],["B",5],["C",23],["B",7],["A",6],["A",3],["C",2]]
d = {"A":0, "B":0, "C":0}

for _____ in l:
    ...
    ...

for key in d:
    print("{}的得分{}".format(key,d[key]))

如果不知道有幾個玩家

In [11]:
l = [["A",10],["B",5],["C",23],["B",7],["A",6],["A",3],["C",2]]
d = {}
for name,score in l:
    if name not in d: # 檢查是否存在這個玩家
        d[name]=0     # 不存在就把他的得分設為預設值0
    d[name]+=score
for key in d:
    print("{}的得分{}".format(key,d[key]))
B的得分12
C的得分25
A的得分19

純文字形式的遊戲計分

  • 可以轉成前面練習題的串列型別
In [12]:
score_log = """
A,10
B,5
C,23
B,7
A,6
A,3
C,2
"""
l = []
for line in score_log.split("\n"):
    print(line.split(","))
    if len(line.split(","))==2:
#     if "," in line:
        name,score = line.split(",")
        l.append([name,int(score)])
print(l)
['']
['A', '10']
['B', '5']
['C', '23']
['B', '7']
['A', '6']
['A', '3']
['C', '2']
['']
[['A', 10], ['B', 5], ['C', 23], ['B', 7], ['A', 6], ['A', 3], ['C', 2]]

應用情境:正規化

  • 星期、月份
  • 中文數字 (eg. 三、參、叄)
In [13]:
datetime = "Oct 18"
month,date = datetime.split(" ")
month = month.replace("Oct","10")
format_date = "{}/{}".format(month,date)
print (format_date)
10/18
In [14]:
datetime = "Oct 18"
month,date = datetime.split(" ")
month_dict = {
    #......
    "10":["Oct","October"],
    "11":["Nov","November"],
    "12":["Dec","December"],
}
for key in month_dict:
    print(key,month_dict[key])
    for keyword in month_dict[key]:
        if month==keyword:
            month = month.replace(month,key)
format_date = "{}/{}".format(month,date)
print (format_date)
10 ['Oct', 'October']
12 ['Dec', 'December']
11 ['Nov', 'November']
10/18

再談字典的處理

建立

直接宣告:在已知資料內容時常用

In [15]:
d = {"A":1, "B":2, "C":3}
print(d)
{'B': 2, 'C': 3, 'A': 1}

先宣告空字典,有需要再給值

In [16]:
d = {}
d["A"]=1
d["B"]=2
d["C"]=3
print(d)
d["A"]=4
print(d)
{'B': 2, 'C': 3, 'A': 1}
{'B': 2, 'C': 3, 'A': 4}

內建函式dict(字典,參數名稱1=value1,參數名稱2=value2...)

  • 字典若不填值,則會生成一新字典;若有填則會更改原值
  • key若不存在會建立;若存在則會修改key指向新value
  • 參數名稱需要符合命名規則
    • 無法以數字當成key
In [17]:
d = dict(A=1,B=2,C=3)
print(d)
d = dict(d,D=4)
d = dict(d,A=5)
print(d)
{'B': 2, 'C': 3, 'A': 1}
{'B': 2, 'C': 3, 'D': 4, 'A': 5}

配合內建函式zip(序列1,序列2)

  • 可讓一序列當成key,另一序列為vale
In [18]:
l1 = ["A","B","C"]
l2 = [1,2,3]
d = dict(zip(l1,l2))
print(d)
{'B': 2, 'C': 3, 'A': 1}

字典.fromkeys(序列,default)

  • 生成一新字典
  • 以序列各元素當成key
  • default 預設為None
In [19]:
l = ["A","B","C"]
d = dict.fromkeys(l)
print(d)
{'B': None, 'C': None, 'A': None}
In [20]:
l1 = ["A","B","C"]
d = {"D":0}
d = d.fromkeys(l1,0) # d 改指向此 新生成的字典
print(d)
{'B': 0, 'C': 0, 'A': 0}

複製

字典.copy()

  • Shallow Copy 淺複製
In [21]:
d1 = {"A":[0,1], "B":2, "C":3}
d2 = d1.copy()
d2["A"][0]=4
d2["B"]=5
print("d1",d1)
print("d2",d2)
d1 {'B': 2, 'C': 3, 'A': [4, 1]}
d2 {'B': 5, 'A': [4, 1], 'C': 3}

刪除

del

  • 刪除key與value的綁定關係
  • 也可以直接刪除命名字典的綁定
In [22]:
a = 1
d = {"A":a, "B":2, "C":3}
print(id(a))
print(id(d["A"]))
del d["A"]
print(id(a))
10105824
10105824
10105824
In [23]:
d = {"A":1, "B":2, "C":3}
del d["A"]
print(d)
del d
print(d)
{'B': 2, 'C': 3}
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-23-057a4fe636f5> in <module>()
      3 print(d)
      4 del d
----> 5 print(d)

NameError: name 'd' is not defined

字典.clear()

  • 清除所有key
In [24]:
d = {"A":1, "B":2, "C":3}
d.clear()
print(d)
{}

字典.pop(key,default)

  • 拿出key指向的value,並刪除
    • 若key不存在,則會回傳default
      • 若沒有設定default,找不到key會報錯
In [25]:
d = {"A":1, "B":2, "C":3}
print(d.pop("A"))
print(d)
print(d.pop("D",4))
print(d.pop("D"))
1
{'B': 2, 'C': 3}
4
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-25-b095244c2c18> in <module>()
      3 print(d)
      4 print(d.pop("D",4))
----> 5 print(d.pop("D"))

KeyError: 'D'

字典.popitem()

  • 回傳key與value組合而成的元組(tuple)
  • 因為字典沒有順序,所以會是隨機拿取
In [26]:
# 隨機取出,因為字典沒有順序
d = {"A":1, "B":2, "C":3}
tmp = d.popitem()
print(tmp,type(tmp))
print(d.popitem())
print(d.popitem())
('B', 2) <class 'tuple'>
('C', 3)
('A', 1)

存值

字典.setdefault(key,default)

  • 若key已存在字典中,則回傳key指向的value
    • 若不存在則建立key與value=default的綁定(回傳的value=default)
      • default 預設為None
In [27]:
d = {"A":1, "B":2, "C":3}
d["D"]=None
print(d.setdefault("E"))
print(d.setdefault("F",4))
print(d.setdefault("C",5))
print(d)
None
4
3
{'F': 4, 'B': 2, 'D': None, 'C': 3, 'E': None, 'A': 1}

原字典.update(新字典)

原字典.update(參數名稱1=value1,參數名稱2=value2...)

  • 根據新字典的內容更新原字典
  • 若新字典有新的key,會在原字典新增
In [28]:
d1 = {"A":1, "B":2, "C":3}
d2 = {
    "B":4,
    "C":5,
    "D":6
}
d1.update(d2)
print(d1)
{'B': 4, 'C': 5, 'D': 6, 'A': 1}
In [29]:
d = {"A":1, "B":2, "C":3}
d.update(B=4,C=5,D=6)
print(d)
{'B': 4, 'C': 5, 'D': 6, 'A': 1}

取值

  • 字典[key]:key不存在會報錯
  • 字典.get(key,default):key不存在會回傳default
    • default 預設為None
In [30]:
d = {"A":1, "B":2, "C":3}
print(d["A"])
print(d.get("A"))
1
1
In [31]:
d = {"A":1, "B":2, "C":3}
print(d.get("D"))
print(d.get("D",0))
print(d["D"])
None
0
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-31-628ad431d9da> in <module>()
      2 print(d.get("D"))
      3 print(d.get("D",0))
----> 4 print(d["D"])

KeyError: 'D'

應用情境:判斷式過於冗長

In [32]:
ticket_type="敬老票" 
if ticket_type=="學生票":
    price=270
elif ticket_type=="敬老票":
    price=250
else:
    price=300
print("{}價格為{}".format(ticket_type,price))
敬老票價格為250
In [33]:
ticket_type="敬老票" 
d = {"學生票":270,"敬老票":250}
price = d.get(ticket_type,300)
print("{}價格為{}".format(ticket_type,price))
敬老票價格為250

補充知識

有預設值的字典

from collections import defaultdict
d = defaultdict(物件型別)
  • 物件型別若不指定,預設為None
  • 並不是原生的字典型別,但有跟原生字典型別同樣的操作方式
  • 就算key不存在,直接用字典[key]也沒關係=>會生成預設值
In [34]:
from collections import defaultdict

print("int()的預設值為",int())
d = defaultdict(int) 
print(d)

print(d["a"])
print(dict(d))
int()的預設值為 0
defaultdict(<class 'int'>, {})
0
{'a': 0}

可以改寫前面的遊戲計分練習題

In [35]:
l = [["A",10],["B",5],["C",23],["B",7],["A",6],["A",3],["C",2]]
d = {}
for name,score in l:
    if name not in d: # 檢查是否存在這個玩家
        d[name]=0     # 不存在就把他的得分設為預設值0
    d[name]+=score
for key in d:
    print("{}的得分{}".format(key,d[key]))
B的得分12
C的得分25
A的得分19
In [36]:
from collections import defaultdict
d = defaultdict(int) 

for name,score in l:    
    d[name]+=score
for key in d:
    print("{}的得分{}".format(key,d[key]))
B的得分12
C的得分25
A的得分19
In [37]:
from collections import defaultdict

print("list()的預設值為",list())
d = defaultdict(list) 

print(d["a"])  
print(dict(d))
list()的預設值為 []
[]
{'a': []}

應用情境-歸類同分學生

In [38]:
from collections import defaultdict
l = [("David",70),("Mars",58),("Kelly",90),("Bob",40),("Amy",58)]
d = defaultdict(list) 
for name,score in l:
    d[score].append(name)

for key in d:
    print("{}分 的學生有 {}".format(key,",".join(d[key])))
40分 的學生有 Bob
58分 的學生有 Mars,Amy
90分 的學生有 Kelly
70分 的學生有 David

有順序性的字典

from collections import OrderedDict
d = OrderedDict()
  • 並不是原生的字典型別,但有跟原生字典型別同樣的操作方式
In [39]:
d = {}
d["A"]=1
d["B"]=2
d["C"]=3
print(d)
{'B': 2, 'C': 3, 'A': 1}
In [40]:
from collections import OrderedDict
d = OrderedDict()
d["A"]=1
d["B"]=2
d["C"]=3
print(d)
OrderedDict([('A', 1), ('B', 2), ('C', 3)])

OrderedDict.move_to_end(key,last)

  • 搬移key順序,
    last預設為True,即把key移至尾端;last=False則是移至首端
In [41]:
from collections import OrderedDict
d = OrderedDict([('A', 1), ('B', 2), ('C', 3)])
d.move_to_end('B')
print(d)
d.move_to_end('B', last=False)
print(d)
OrderedDict([('A', 1), ('C', 3), ('B', 2)])
OrderedDict([('B', 2), ('A', 1), ('C', 3)])

OrderedDict.popitem(last)

  • 因為是有順序的字典,
    last預設為True,即從尾端拿出並移除;last=False則是從首端
In [42]:
from collections import OrderedDict
d = OrderedDict([('A', 1), ('B', 2), ('C', 3)])
print(d.popitem())
print(d.popitem(False))
print(d)
('C', 3)
('A', 1)
OrderedDict([('B', 2)])

注意!

  • 參數不具備順序性
In [43]:
from collections import OrderedDict
d = OrderedDict([('A', 1), ('B', 2), ('C', 3)],D=4,E=5,F=6)
print(d)
OrderedDict([('A', 1), ('B', 2), ('C', 3), ('F', 6), ('E', 5), ('D', 4)])
In [44]:
from collections import OrderedDict
d = OrderedDict([('A', 1), ('B', 2), ('C', 3)])
d.update(D=4,E=5,F=6)
print(d)
OrderedDict([('A', 1), ('B', 2), ('C', 3), ('F', 6), ('E', 5), ('D', 4)])

字串格式化

In [45]:
data = {"name":"Mars","birth":1990}
print("{name}{birth} 年生 ".format(name="Mars",birth=1990))
print("{name}{birth} 年生 ".format(**data))
Mars 是 1990 年生 
Mars 是 1990 年生 

學習資源對應

  • Python 程式設計入門 (適用於 2.x 與 3.x 版) 葉難
    • 05 字典與集合
      • 5.1 字典與集合的基礎:字典
      • 5.3 字典:除了keys()、values()、items()
      • 5.5 補充:有預設值的字典、具有順序性的字典
  • The Python Standard Library(Python 3.6.2)
    • 4.10. Mapping Types — dict
      • 除了 4.10.1. Dictionary view objects
    • 8.3. collections — Container datatypes
      • 8.3.4. defaultdict objects¶
      • 8.3.6. OrderedDict objects