路遙知碼力,日久練成精-只要在程式之路鑽研的夠深,便能夠充分發揮程式碼的力量; 練習的日子夠久,便能夠練成寫出精簡代碼的能力。
進入「列表生成式」這個單元之前,
我們先來聊聊python資料型態之間的轉換。
這是python非常方便的地方,
在python中要轉換資料型態,
沒有繁複的語法,
只要把要轉換的資料放進對應的函數裡就行了。
例如有一個字串:
s = "0025"
我們可以轉換成不同型態:
>>> int(s) #轉換成整數
25
>>> float(s) #轉換成浮點數
25.0
>>> list(s) #轉換成列表
['0', '0', '2', '5']
>>> tuple(s) #轉換成元組
('0', '0', '2', '5')
>>> set(s) #轉換成集合
{ '0', '2', '5'}
>>> dict(s) #字串無法直接轉換成字典,會報錯
ValueError
你可能會說要轉換成不同資料型態有什麼好處嗎?
看起來不就是資料用小括號、中括號還是大括號來裝的區別嗎?
不不,這樣想就錯了,
Python的容器不光是看起來長的不一樣而已,
而是真的在屬性上有所不同。
拿日常生活中的「容器」打個比方吧,
就像你平常不會拿馬克杯來泡泡麵,
拿大碗公來泡咖啡,
Python內的容器也有它們適合使用的時機。
舉最簡單的例子好了,
像是字串的加號和數字的加號就有所不同。
>>> 12+34
46
>>> '12'+'34'
'1234'
這些不同資料型態上有什麼不同的屬性呢?
我們來一一介紹。
在Day7的範例7-2撲克花色中,
我們曾見過一次字典的用法
不知道大家小學有沒有查過字典呢?
由於現在網路太發達了,
不一定人人都摸過實體字典,
這邊以線上的教育部國語字典為例(圖示為查詢「字典」這個詞的意思):
當你不知道一個詞語的意思時,
你會輸入關鍵字(key)在字典中查詢,
然後便可以查詢那個詞語的解釋(value)。
這種查詢的概念便類似python的字典(dict),
python的dict
由一組key:value
組成放在大括號裡,
例如:
D = {'s':"黑桃", 'h':"紅心", 'd':"方塊", 'c':"梅花"}
此時我們便建立了一本字典,
可以想成是「詞語's'
的意思為"黑桃"
」,「詞語'h'
的意思為"紅心"
」…。
當我們想要查詢某個字的意思,
便可以用中括號放入關鍵字做查詢,如:
>>> D['s']
'黑桃'
介紹第一個屬性,
什麼是可變物件與不可變物件呢?
不可變物件,該物件所指向的記憶體中的值不能被改變。
例如: str, int, float, tuple屬於不可變物件。
可變物件,該物件所指向的記憶體中的值可以被改變。
例如: list, set, dict都屬於可變物件。
說起來不難,但事實上不可變的屬性可是困惑了很多新手,
到底是什麼不可變呢?
有新手會舉例這樣的程式碼:
a=0
a=1
一開始我把a的值設成0,
現在把a的值改為1了,
應該是可以改變啊?
事實上,改變不可變型態變數的值的時候,
並不是直接去改變那個值,
而是創造一個新的地址,
再把變數指向那個地址。
我比較喜歡用「貼標籤」來做比喻,
一開始做a=0
,就好像把a這張標籤貼到0這個物件上, a=1
就只是把a這張標籤撕下來貼到1這個物件上。
對於原來0這個數字來說,
0的頭上並不會突然長角或有什麼改變。
我們看tuple和list的例子更清楚,
我們各自創建一個list和tuple,內容都是1,2,3:
L=[1,2,3] #list
M=(1,2,3) #tuple
L[0]=2 #L會變成[2,2,3]
M[0]=2 #這一行會出現TypeError
上面程式中,L是list而M是tuple,
可以做 L[0]=2
去改變L的內容,
但是 M[0]=2
的操作是禁止的。
另一個常見的誤解如下:
s="Hello"
s+="World"
print(s) #印出結果HelloWorld
咦?這邊可以做 s+="World"
耶?
乍看之下不是改變了這個字串嗎?
怎麼會說字串不可變呢?
其實因為字串是不可變的特性,
s+="World"並不是改變原本的字串,
而是s指向了一個新的物件,叫做"HelloWorld"。
我們再延伸Day3範例3-2,3-3冰箱的例子來幫助理解可變變數:
還記得Day3時我們舉過這個例子嗎?
這回你調皮的室友又要偷動冰箱囉。
令fridge這個變數記錄冰箱存放的東西:
fridge = ['蛋糕', '蘋果', '香蕉']
b = fridge
b.remove('蛋糕')
print(fridge) #['蘋果', '香蕉']
當修改b裡面的值內容時,
fridge的內容也會真的受到影響。
現在我們試著寫兩隻類似的程式出來,
一個是列表版本的,
一個是字串版本的,
實測看看修改可變物件和不可變物件是否有差別。
首先是列表版本的:
#列表版本 - 冰箱
fridge = ['蛋糕', '蘋果', '香蕉']
b = fridge
b+= ['臭掉的牛奶']
print(fridge) #['蛋糕', '蘋果', '香蕉', '臭掉的牛奶']
再來是字串版本的:
#字串版本 - 冰箱
fridge = '蛋糕, 蘋果, 香蕉'
b = fridge
b+= ', 臭掉的牛奶'
print(fridge) # '蛋糕, 蘋果, 香蕉'
在這兩個例子,我們都做了類似的操作,
同樣都使用b = fridge
賦值,
也同樣用b+= …
來改變變數b
的內容,
但只有列表版本的冰箱受到影響,
字串版本的冰箱則不受影響。
為什麼有這樣的差別呢?
讓我們上個圖示說明:
<列表版>
記得當我們做b = fridge
這一行時,
猶如將標籤b 貼到fridge這個冰箱上,
此時這兩個變數指的是同一個冰箱了,如圖:
由於列表是可變物件,
執行 b+= ['臭掉的牛奶']
時,
便會直接原地修改冰箱的內容,如圖示:
<字串版>
但字串為不可變物件,
在做 b+= ', 臭掉的牛奶'
這項操作時,
由於無法更改原字串'蛋糕, 蘋果, 香蕉'
的內容,
只好先創建一個新的字串叫做 '蛋糕, 蘋果, 香蕉, 臭掉的牛奶'
,
再把標籤b撕下來貼到新字串上了。
理解完可變物件與不可變物件後,
剩下的概念就相對簡單了。
迭代是利用迴圈遍歷把元素取出來的過程,
利如str, list, set, tuple, dict都是可迭代物件,
int, float為不可迭代物件。
簡單來說,可迭代物件可以進行for… in … 操作取出元素。
舉例來說,
我們可以遍歷一個字串,
印出每個字元:
s= "Hello"
for c in s:
print(c)
結果為H
e
l
l
o
再看一例,
我們們這次試著遍歷一個字典:
D={1:'A',2:'B'}
for i in D:
print(i)
結果為1
2
(字典的迭代會遍歷key值)
這邊指的可重複性指的是容器裡面是否可以有重複的元素。
list 和tuple 重複,
而set和dict不可重複。
例如我們宣告一個初始值有重複元素的集合S:
S={1,2,2,2,5}
print(S)
結果為 {1, 2, 5}
,
集合會自動去除重複的元素。
有序與無序指的是元素的順序在容器中不同是否有差別。
list 和tuple 有序,
而set和dict無序。
我們看一個例子:
print([1,2,3]==[2,3,1]) #False
print({1,2,3}=={2,3,1}) #True
我們試著判斷[1,2,3]是否與[2,3,1]相等,
因為列表是有序的,元素順序不同則結果視為不同,
而集合是無序的,不論順序怎麼寫都視為相同。
有了內建型態方便的轉換,我們來看幾個應用吧。
給你一個整數,回傳整數倒轉之後的結果。
例如:
123 -> 321
2300 -> 32 (原來倒轉後變0032,但因為是整數,前面有0需去除)
-250 -> -52(負數倒轉先倒轉數字部分,再補上負號)
0 -> 0
這個問題看似複雜,
又要考慮正負號,又要考慮前面有0需去除。
還記得Day5的時候我們有學過字串倒轉只需一行就解開了嗎?
>>> s="abc"
>>> s[::-1] #倒轉字串
'cba'
數字本身不能做切片運算,
但是如果我們轉換成字串的話,
就可以輕鬆把數字倒轉了:
>>> n= 123
>>> str(n)[::-1]
'321'
>>> n= 2300
>>> str(n)[::-1]
'0032'
最後再把倒轉後的字串轉回數字即可:
>>> n= 2300
>>> int(str(n)[::-1])
32
很方便地,用int()轉換為整數時,連前面的0它都會自動去除。
但是我們需要把正負號考慮進去,
例如n若為 -123,直接轉成字串倒轉將會得到 '321-',
負號會跑到最右邊,這並不是我們要的結果。
因此,我們可以先將正負號取出來:
sign= '-' if n<0 else ''
倒轉數字時,可以統一取絕對值再操作,
完整實作程式碼如下:
def reverseNum(n):
sign= '-' if n<0 else ''
return int(sign + str(abs(n))[::-1])
# 底下測試結果
print(reverseNum(123)) #321
print(reverseNum(2300)) #32
print(reverseNum(-250)) #-52
print(reverseNum(0)) #0
明明買了一款電動遊戲,
總共有100道關卡,編號1~100,
該遊戲可以不必按照順序破關,
也可以重複遊玩。
給你一個列表表示明明的遊玩關卡順序,
你能幫它檢查明明是否玩到重複的關卡嗎?
實作一個函數名稱為 checkRepeat(plays),
讀入參數plays表示遊玩順序,
例如:
1. input: plays=[80, 11, 86, 45, 54, 31, 46, 11, 61, 42],
第十一關重複玩了兩次,
函數要回傳True。
2. input: plays= [54, 35, 69, 52, 29, 5, 4, 45, 23, 84],
沒有重複的關卡,
函數要回傳False。
嗯,給你個參考想法,
問題要判斷列表內有沒有重複的元素,
因此,你可能會想去數每個在列表裡面的元素,
如果元素出現超過1次,則表示有重複。
實現程式碼如下:
def checkRepeat(plays):
for p in plays:
if plays.count(p)>1:
return True
return False
但是這樣還不夠精簡,
你能否使用適當的容器轉換來完成這一題呢?
(提示: 利用set()可以去除重複元素的特性)