iT邦幫忙

1

【python超入門】(14)字串、列表的簡便語法: 串接(+與*)、負索引、切片、列表生成式

哈囉,大家好,
在教學【Python 超入門】(4) 變數型態-list: 能納百物的百寶袋中,
我們已經介紹過python中基礎資料型別- 列表,
今天要再介紹字串、列表的簡便語法,
學會的話可以讓你的python程式寫的更簡潔

加法、乘法用於字串與列表

一般來說,我們覺得加減乘除好像是數字的專利,
然而在python中,列表和字串是有加法和乘法可以用的,
讓我們能夠更方便的操作列表和字串

譬如說如果我們希望有一個字串s
字串的內容是"Hello"連續寫五次可以怎麼寫呢?

第一個方法就是乖乖把"Hello"打五次,寫:

s="HelloHelloHelloHelloHello"

但是這樣寫有個明顯的缺點,
若你需要寫一百次"Hello"將會非常占篇幅,
並且如果想要把"Hello"寫i次(i是變數)則沒辦法處理

因此通常用for迴圈是比較好的解法:

s=""
for i in range(5):
    s = s + "Hello"
# s 的值變成"HelloHelloHelloHelloHello"

但這樣似乎仍顯囉唆,
python有很方便的運算子,加法(+)和乘法(*),
如果用在字串或列表上的意思便是「串接」和「重複」。
上述程式其實只需一行,再也不必打一堆迴圈了:

s="Hello" * 5

接著示範更多例子:

>>> "234"+"abc"
'234abc'
>>> "2"+"3"
'23'
>>> "真" * 5 + "高興見到你"
'真真真真真高興見到你'

融會貫通,加法(+)和乘法(*)亦可以用在列表上:

>>> [2,4]+[5,6,9]
[2, 4, 5, 6, 9]
>>> [1] * 6
[1, 1, 1, 1, 1, 1]

有了這樣方便直覺的運算子,
其實可以非常方便的幫助我們繪製各種有趣的圖形

範例: 階梯圖形

教大家如何在螢幕上印出階梯圖形,
我們希望在螢幕上顯示7階的階梯,如下:

*
**
***
****
*****
******
*******

大家可以先停下來想一想怎麼做。












想好了嗎?有字串的重複運算應該不會太難,
觀察圖形,我們要
在第一行印出1顆星星,
在第二行印出2顆星星,

在第i行印出i顆星星。
因此用一層for迴圈遍歷即可:

for i in range(1,8):
    print('*' * i)

範例: 金字塔圖形

教大家如何在螢幕上印出階梯圖形,
我們希望在螢幕上顯示7階的金字塔,如下:

      *
     ***
    *****
   *******
  *********
 ***********
*************

大家也可以先停下來想一想怎麼做。












我們一樣觀察圖形,
第i行需要印的空格數= 總行數 - i
第i行需要印的星星數= 2*i -1
程式如下:

for i in range(1,8):
    print(' ' * (7-i) + '*' * (2*i-1))

負索引; 切片語法(Slice)

我們複習一下列表的取值,例如我們有一個列表,表示早餐店的菜單:

L = ["香雞堡", "豬肉堡", "牛肉堡", "總匯三明治", "鮪魚三明治", "火腿三明治"]
>>> L[0]
"香雞堡"
>>> L[1]
"豬肉堡"
>>> L[-1]
"火腿三明治"

L[0]是從左邊數起的第一個元素,
L[1]是從左邊數起的第二個元素,

另外,python也可支援你直接寫負數的index,
L[-1]是L的倒數第一個元素,
L[-2]是L的倒數第二個元素,

以此類推

切片介紹

那如果我們想要取得前三個元素所形成的列表呢?
一樣L = ["香雞堡", "豬肉堡", "牛肉堡", "總匯三明治", "鮪魚三明治", "火腿三明治"]
我們想要得到子列表["香雞堡", "豬肉堡", "牛肉堡"]

(BAD)方法一、直接寫

最普通的方法便是直接取值:

foods= [L[0], L[1], L[2]] # ["香雞堡", "豬肉堡", "牛肉堡"]

可是假如陣列很大,
我們想要取的是前一百個元素,
抑或要取前i個元素(i是變數),
這樣寫就不切實際了

(BAD)方法二、for迴圈

第二招是使用for迴圈,應該也不難想:

foods=[]
for i in range(3):
    foods.append(foodArr[i])

但是對python的精簡語法來說,用for迴圈又稍嫌繁瑣,
python的切片操作符就是用來簡化這種操作而設的

方法三、切片語法

切片操作符的基礎語法:

newArr = L[起始位置(包含) : 結束位置(不包含): 步長]

其中L可以是列表或是字串或是元組
L[起始位置(包含) : 結束位置(不包含): 步長] 的意思為,
從L這個列表,取出範圍為L[起始位置] ~ L[結束位置-1]之間的數字,
每步長個間隔取一次,
若步長這個參數省略則預設為1

也就是說,上述取出前三個元素的動作,
可以簡單用切片來取:

L=["香雞堡", "豬肉堡", "牛肉堡", "總匯三明治", "鮪魚三明治", "火腿三明治"]
foods= L[0:3]

如果第一個參數是0,可以省略:

>>> L[:3]
["香雞堡", "豬肉堡", "牛肉堡"]

若第二個參數省略,表示從起始位置直接取到列表的結束,
例如:

>>> L[3:]
['總匯三明治', '鮪魚三明治', '火腿三明治']

因此,舉一反三一下,
第一個參數和第二個參數同時省略,
就可以得到整個列表的複製(表示從第0個元素取到最後一個)

>>> L[:]
['香雞堡', '豬肉堡', '牛肉堡', '總匯三明治', '鮪魚三明治', '火腿三明治']

切片搭配負數index使用

小馬有教過可以用一個負數取得陣列倒數的元素,
L[-1]是L的倒數第一個元素,
L[-2]是L的倒數第二個元素,

因此,如果需要取得陣列倒數兩個元素,則可以這樣寫:

>>> L[-2:]
['鮪魚三明治', '火腿三明治']

試試更進階的,搭配步長做使用:

>>> L[1:5:2] #從index = 1的元素開始,每2個取一次,直到index = 5的元素(不包含)
['豬肉堡', '總匯三明治']

想要更了解切片語法的話,建議可以自己宣告一個大一點的列表,
然後試著改參數試試會更清楚

重要觀念: N = L v.s. N = L[:]

這邊宣告L = ['蛋糕', '蘋果', '香蕉']
剛剛在介紹切片語法時,有提到寫L[:]會得到切片的複製,
請看例子:

L =  ['蛋糕', '蘋果', '香蕉']
N = L[:]
print(N) # ['蛋糕', '蘋果', '香蕉']

但是寫L[:]看起來好像比較不好懂,
這時有人會問啦,
第二行可不可以乾脆寫成N = L?

事實上,N = LN = L[:]是完全不同的意思,
N = L直接賦值,會把列表L所在的記憶體位置直接給N
N = L[:]才是複製一個列表,將L的元素複製一份給N

小馬知道看文字敘述你們應該有看沒有懂,
小馬畫兩張圖應該會清楚許多

N = L的效果

若直接寫N = L,N和L兩個變數會指向同一個列表
https://ithelp.ithome.com.tw/upload/images/20200409/20117114yPxavwT45e.png

N = L[:]的效果

若寫N = L[:],則是會將L的元素複製一份給N
https://ithelp.ithome.com.tw/upload/images/20200409/20117114pKz2vhoMBC.png

再給個實例應該就明瞭了

程式範例

若是N = L的情況,因為N和L指向同一個列表,修改N的元素會影響L

L =  ['蛋糕', '蘋果', '香蕉']
N = L
N[1] = '葡萄'
print(L) # ['蛋糕', '葡萄', '香蕉']

若是N = L[:]的情況,因為N只是複製L的元素,修改N的元素不會影響L

L =  ['蛋糕', '蘋果', '香蕉']
N = L[:]
N[1] = '葡萄'
print(L) # ['蛋糕', '蘋果', '香蕉']

最後看個切片有趣的應用吧~

應用: 反轉一個字串

文字充滿著奧妙,不論中、英文,
有些字倒著看也具有意思,
例如:

刷牙<->牙刷
國王<->王國
dog<->god
desserts(甜點) <-> stressed(壓力)
live(生命) <-> evil(邪惡)

你能否利用切片運算符實現字串倒轉的函數呢?

其實很簡單,要倒著取字只要步長設為負數即可,
又因要取整個字串,切片的第一、二個參數都可以省略。
實作如下:

def reverseStr(s):
    return s[::-1]

print(reverseStr("desserts")) # stressed
print(reverseStr("刷牙")) #牙刷

列表生式式語法(List Comprehensions)

列表生成式(List Comprehensions),顧名思義
,為運用一個列表生出另一個列表的語法
基礎語法如下:

新列表 = [something for x in 舊列表(或可迭代物件)] 

相當於

新列表 = []
for x in 舊列表(或可迭代物件):
    新列表.append(something)

這樣的寫法。 (註: append()是在列表後面添加新元素的意思)

在列表生成式語法中,
直接把for迴圈放進列表中以簡化程式碼

另外,有時候我們會想要取出列表內滿足條件的元素,
而非取出所有元素,
這時可以直接在「列表生成式」後面加上if語句,
決定篩選的條件。
語法大致上如下:

新列表 = [something for x in 舊列表(或可迭代物件) if 條件判斷句] 

來看幾個簡單的例子

看看列表生成式的各種應用

生成1~10的平方數

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

將字串轉為字元陣列

>>> s="Hello"
>>> [c for c in s] #亦可直接寫 list(s)
['H', 'e', 'l', 'l', 'o']

雙for迴圈生成全排列

>>> [m + n for m in 'abc' for n in '123']
['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']

取出列表中的偶數

>>> L=[18, 73, -24, 6, 34, -87, -17, -98, -53, 16]
>>> [x for x in L if x%2==0]
[18, -24, 6, 34, -98, 16]

取出列表中的負數

>>> [x for x in L if x<0]
[-24, -87, -17, -98, -53]

取出列表中的字串型態

(註: 內建函數isinstance(x, type)可以判斷前者是否為後者的變數型態)

>>> M = ["Horse","Tiger",25,6.2,"Tomato"]
>>> [x for x in M if isinstance(x, str)]
['Horse', 'Tiger', 'Tomato']

簡單吧?
凡是運用可迭代容器的原始資料(如: 字串,元組,列表,字典),
要生成一個新列表,
都很適合使用列表生成式。

融會貫通: range, 切片, 列表生成式

切片與列表生成式都可以在python中從一個列表生出另外一個列表,
你會發現切片與列表生成式的語法還蠻像的,其實可以一起記

切片操作符的基礎語法為:

newArr = L[起始位置(包含) : 結束位置(不包含): 步長]

range()函數的基礎語法為:

range(起始位置(包含), 結束位置(不包含), 步長)

列表生成式的基礎語法為:

新列表 = [something for x in 舊列表(或可迭代物件,常常使用range)] 

如果有一個列表L(長度>21),我們想要從第四個元素開始,
每三個取一次,直到第21個元素停下來,
那可以怎麼寫呢?(即想要得到新的列表是[L[4], L[7], L[10], L[13], L[16], L[19]])
這個例子下,切片與列表生成式都可以用,
而且語法上還蠻像的

例子
range(4,21,3) 表示數字範圍 4,7,10,13,16,19
L[4:21:3] 則表示切片的列表 [L[4], L[7], L[10], L[13], L[16], L[19]]
[L[i] for i in range(4,21,3)] 亦可以表示[L[4], L[7], L[10], L[13], L[16], L[19]]


尚未有邦友留言

立即登入留言