iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 11
0
Software Development

從零開始學Python系列 第 11

[Day 11] 從零開始學Python - 標準程式庫:你的機車不機車,載我瀏覽世界景色

註:本文同步刊載在Medium,若習慣Medium的話亦可去那邊看呦!

先來解答昨天的問題吧!

  1. 我們可以使用randint(1, 100),或者使用int(1 + random() * 100)來取得亂數值。
from random import randint
ans, guess = randint(1, 100), 0
l, r = 1, 100
while ans != guess:
    try:
        guess = int(input('\n請在' + str(l) + '到' + str(r) + '之間猜一個數:'))
    except: # 加入continue,直接跳過後面的範圍處理,回到迴圈的開頭
        print('請輸入正常的數字,不要加其他字母或符號呦!')
        continue
    if guess < l or guess > r: # 超出範圍的部分同樣也要跳過(當然,也可以用elif和下面的判斷連起來)
        print('請輸入正確範圍內的數字!')
        continue
    if guess < ans:
        print('您猜的數字比答案還要小,請再猜大一點~')
        l = guess + 1
    elif guess > ans:
        print('您猜的數字比答案還要大,請再猜小一點~')
        r = guess - 1
print('恭喜你猜出答案啦!')
  1. 為了要避開,我們可以先生成一個串列,範圍為1~100並且去除掉avoid_lt的值。
from random import choice
avoid_lt = [4, 14, 44, 94]
ans_lt = [i for i in range(1, 101) if i not in avoid_lt] # 在列表生成式中使用if來處理要跳過的值
ans, guess = choice(ans_lt), 0

在上一篇我們用了一些匯入的模組及套件以後,
今天我們再花一點時間進一步來介紹Python的標準程式庫!
在Python當中有很多已經做好的函式,只需要匯入就可以使用囉!
在實作一些功能前,有時候不妨先查一下是否有適用的函式可用。

但有一點很重要的要先提到:
請不要造一輛車子以後只拿輪子,這會很沒效率。
為什麼呢?
因為當我們造一輛車子的時候會耗費大量的時間,
但如果我們只是要一個輪子的話,
我們應該去尋找專門製作輪子的方式,而非造車拔輪。
所以讀者在使用函式的時候,都要盡量去留意當前自己的目標是什麼,
避免耗費過多時間讓Python在不必要的部分計算。

首先我們來介紹defaultdict()
先前我們介紹過字典,假設我們現在有一個字典名為dic,
當我們使用dic[key]時,如果這個key並不在字典裡,
會發生什麼事情呢?
答案是:會產生錯誤!

>>> dic = {}
>>> dic['XD']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'XD'

為了處理這個問題,我們有幾種方式:

  1. 每次檢查if key in dic,存在的話,再做其他的事情。
  2. 如果我們有一些預設的狀況的話,例如要存放key出現的次數,那可以使用get()
    get()的第一個參數放入要找的key,第二個位置放入預設的value值,
    如果這個key不存在於字典,就使用後面給定的value做為結果;
    如果存在的話,當然就用對應到的value。
>>> dic.get('XD', 0) 
0
  1. defaultdict()
>>> from collections import defaultdict
>>> cnt = defaultdict(int)
>>> cnt[3]
0
>>> cnt['XD']
0
>>> cnt['a']=1
>>> cnt.get('a')
1
>>> cnt.get('XD')
0

在defaultdict中,當key沒有被指定value時,
就會採用初始化時預設給進去的東西:
寫int代表預設值為0、寫list代表預設為空串列、寫dict表預設為空字典...以此類推。
省略參數的時候,會被設定為None,也就是什麼都沒有的意思,
但這麼一來,就又會回到一般字典對於不存在的key會報錯的狀況。
給進去的東西還有其他的用法,有興趣的讀者也可以再深入研究。

接著我們來看看Counter()
顧名思義,Counter()基本上就是用來計算什麼東西出現幾次用的,
它是屬於collections裡面的一部分,
我們直接來看範例:

>>> from collections import Counter
>>> scores = [35,70,10,20,35,70,70]
>>> score_counter = Counter(scores) # 將list代入,即可產生一個Counter型態的東西
>>> score_counter
Counter({70: 3, 35: 2, 10: 1, 20: 1}) # 為當中的元素計數
>>> names = ['James', 'Michael', 'Ted', 'James', 'Leo']
>>> Counter(names) # str也可以使用
Counter({'James': 2, 'Michael': 1, 'Ted': 1, 'Leo': 1}) 
>>> score_counter.most_common() # most_common()會按由大到小的出現次數來排序
[(70, 3), (35, 2), (10, 1), (20, 1)]
>>> score_counter.most_common(2) # 代入的值代表要取幾個數
[(70, 3), (35, 2)]

除了範例提到的基本以外,兩個Counter之間可以做加、減,
結果就是將對應的key出現的次數加總及相減。(相減時小於等於0的部分會去掉)
使用&和|則會像set一樣取得交集和聯集。

前面包含了dict及defaultdict的部分,
都有一個特點:它不一定按照原先順序排列。
那麼如果你真的很在意一開始的順序呢?
我們可以使用OrderedDict()來處理。

>>> scores = OrderedDict([('James', 80), ('Andy', 70), ('Curry', 100)]) # 每組要用tuple處理
>>> scores
OrderedDict([('James', 80), ('Andy', 70), ('Curry', 100)]) # 按進去的順序,取出也會相同
>>> for s in scores:
...     print(s)
...
James
Andy
Curry

當我們在練習LeetCode寫題目時,
常常會需要使用到stack(堆疊)或queue(佇列)這兩種資料結構,
前者通常使用list即可,後者則一般會使用deque
(理論上念做deck,但我都習慣念de-queue)

deque是一個雙向的序列,
可以從開頭或結尾傳入或取出資料。
我們直接來看範例:

>>> from collections import deque
>>> s = 'abcde'
>>> deque(s) # 直接將字換成deque
deque(['a', 'b', 'c', 'd', 'e'])
>>> d = deque(s)
>>> scores
>>> d.popleft() # 從左邊取出
'a'
>>> d.popleft()
'b'
>>> d.pop() # 從右邊取出
'e'
>>> d.append('XDFES')
>>> d
deque(['c', 'd', 'XDFES'])
>>> d.appendleft('YES')
>>> d
deque(['YES', 'c', 'd', 'XDFES'])

由於從右邊處理才是一般常見狀態,
所以無論是append或pop,
在往左時都會加上一個left,
藉以區分兩者差別。

如果對於資料結構、演算法、LeetCode有興趣的話,
也歡迎看看去年的鐵人賽發文,或者查筆者的Medium系列文章!
由於這些在解題或日常算是蠻常用的,後續會再花一些篇幅來仔細介紹這些資料結構呦!

以上只不過是Python內建函式庫的冰山一角而已XD
有興趣的讀者,可以再自己找看看有什麼你需要的東西~

那我們來練習一下題目吧!

  1. 給定兩個字串s跟t,已經知道t的組成,
    是將s的字母打亂以後進行重組,再隨機加上一個字母。
    請用前面所學,找出被加上的那個字母。

辛苦啦!我們明天見!


上一篇
[Day 10] 從零開始學Python - 模組與套件:借一段往日旋律,宛轉悠揚
下一篇
[Day 12] 從零開始學Python - 物件與類別:我們不一樣,每個人都有不同的際遇(上)
系列文
從零開始學Python30

尚未有邦友留言

立即登入留言