2017年5月5日 星期五

MicroPython on ESP8266 (三) : 序列型別測試

一直想要好好地對 CPython 測試一番都未動手, 這次因為測試 ESP8266 上的 MicroPython 之便, 乾脆連 CPython 也一併測試以資對照, MicroPython 沒有實作的部分再另外單獨進行測試, 此系列的前兩篇參考 :


Python 常用的序列型別有下列三種, 均繼承自 sequence 這個抽象型別, 它們的元素都是照順序存放的 :
  1. list (串列)
  2. tuple (元組)
  3. str (字串)
其中只有 list 是可變 (mutable) 資料型別, 其資料內容可以變更; 而 tuple 與 str 都是不可變 (immutable) 資料型別, 物件一旦建立就無法更改其內容. 序列型別物件的共同特性就是其元素都是有序的, 都可以利用索引來擷取其元素. 另外, 因 sequnce 型別繼承自 container 型別, 所以序列型別的物件也都是容器型別物件, 其繼承關係如下 :


Python 有五種容器型別 : str, list, tuple, dict, 以及 set, 容器皆以 cell 為基本儲存單位, 除了 str 只能儲存 unicode 字元外, 其他四種都可以儲存不同型態 (異質) 的資料.

一. 字串 (str) :

1. 字串的建立 :

Python 3 的字串全面採用 Unicode 表示, 不必像 Python 2.x 那樣在處理 Unicode 字串時還要在前面冠一個 "u". Python 的字串使用成對的單引號或雙引號括起來, 例如 :

>>> "Hello World!"
'Hello World!'
>>> 'Hello World'
'Hello World'
>>> print("Hello World!")
Hello World!
>>> print('Hello World!')
Hello World!

如果字串中必須用到引號本身, 可以用另一種引號作為外引號括起來, 例如 :

>>> print("It's mine")
It's mine
>>> print("he said:'Go!'")     #雙引號當外引號
he said:'Go!'
>>> print('he said:"Go!"')     #單引號當外引號
he said:"Go!"

或者可以使用跳脫 (Escape) 字元 "\" 來轉義 (改變字元原有意義), 原本引號是字串的開始或結束字元, 前面加倒斜線將其改變為引號本身 :

>>> print('It\'s mine')
It's mine
>>> print('he said:\'Go!\'')
he said:'Go!'
>>> print("he said:\"Go!\"")
he said:"Go!"

Python 的跳脫字元包括 \' 與 \" 總共有 12 個如下 :

 跳脫字串 說明
 \' 單引號
 \" 雙引號
 \\ 倒斜線
 \n 換行 (LF)
 \r 回車 (CR)
 \t 水平定位 (TAB)
 \v 垂直定位 (VT) 
 \a 響鈴 (BEL) 
 \b 退格 (BS) 
 \f 插入一頁 (FF) 
 \x41 16 進位 ASCII  碼 A
 \101 8 進位 ASCII  碼 A

這些跳脫字元大都是 ASCII 字元編碼中的控制字元, 例如 :

>>> print('123\\456\n\r789')       #印出倒斜線與跳行
123\456
789
>>> print('123\\456\n789')          #只用 \n 即可跳行
123\456
789
>>> print('123\\456\r789')           #\r 只有回車功能, 不會跳行
789\456    

可見跳行可以用 \n\r 或者單用 \n 皆可, \r 只是像打字機那樣的單純回車 (carrage return) 而已, 所以上面最後一例 \r 會讓列印位置回到最前面印出 789, 就把原先已經印出的 123 蓋掉了.

>>> print('123\t456\t789')    #水平定位
123     456     789
>>> print('123\v456\v789')  #垂直定位
123
   456
      789

換頁字元 \f  會刷新頁面 (換上新頁),  從最上面開始列印, 響鈴字元 \a 會讓系統發出噹的一聲, 而退位字元會往回一格列印, 蓋掉前一字元, 例如下面範例中開頭的 \f 會換新頁, 從第一列開始印出 0, 接著響鈴並印出 123, 再響鈴後印出 456, 然後 \b 會倒退一格到 6 的位置, 印出 789, 所以 6 就被蓋掉不見了 :

>>> print('\f0\a123\a456\b789')
012345789

下面範例測試 8 進位與 16 進位的跳脫字元表示法, 字母 A 的 ASCII 碼以 8 進制表示為 101, 以 16 進制表示則為 41. 注意 16 進制必須用小寫 x, 不可用大寫 X :

>>> '\101'
'A'
>>> '\x41'  
'A'

Python 有兩個內建函數用來處理 ASCII 碼 :

 函數 說明
 ord(char) 傳回字元 char 的 ASCII 碼 (10 進制)
 chr(code) 傳回 ASCOII 碼 code 的字元

注意, ord() 的參數只能傳入一個字元, 且傳回值是 10 進位的 ASCII 碼, 要取得 8 進制與 16 進制 ASCII 碼須使用 oct() 與 hex() 轉換, 例如 :

>>> ord('A'), oct(ord('A')), hex(ord('A'))
(65, '0o101', '0x41')
>>> ord('\n'), oct(ord('\n')), hex(ord('\n'))
(10, '0o12', '0xa')
>>> chr(65), chr(0o101), chr(0x41)
('A', 'A', 'A')
>>> ord('AB')        #ord 只能傳入一個字元
Traceback (most recent call last):
  File "<stdin>", line 1, in TypeError: ord() expected a character, but string of length 2 found

當字串中含有大量跳脫字元時會使可讀性變差, 這時可以用 Python 的原始字串 (raw string) 來表示, 在字串的開頭引號前面加個 r 就表示引號內為原始字串, 例如 :

>>> '\n'
'\n'
>>> r'\n'      #引號內為原始字串, \ 會以 \\ 取代
'\\n'
>>> r'\\'      #引號內為原始字串, \ 會以 \\ 取代
'\\\\'
>>> R'\n'     #MicroPython 未實作大寫 R 的原始字串功能
Traceback (most recent call last):
  File "<stdin>", line 1
SyntaxError: invalid syntax

注意, Cpython 可用 r 或 R, 但 MicroPython 沒有實作 R, 只能用 r : 

C:\Users\Tony>py -2
Python 2.7.8 (default, Jun 30 2014, 16:03:49) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> R'\n'
'\\n'
>>> R'\\n'
'\\\\n'

原始字串在表示 Windows 的路徑時可以派上用場 (使用倒斜線做分隔符號), 例如下面範例中, 字串中的 \U 會被認為要跳脫, 但又無法解譯這個 \U 跳脫字元, 所以產生執行造成錯誤, 前面冠 r 將其變成原始字串後就可以取消跳脫功能了 :

>>> 'C:\Users\tony'             #沒有 \U 這個跳脫字元
Traceback (most recent call last):
  File "<stdin>", line 1
SyntaxError: invalid syntax
>>> r'C:\Users\tony'            #用 r 避免跳脫
'C:\\Users\\tony'
>>> print('C:\Users\tony')   #沒有 \U 這個跳脫字元
Traceback (most recent call last):
  File "<stdin>", line 1
SyntaxError: invalid syntax
>>> print(r'C:\Users\tony')  #用 r 取消跳脫
C:\Users\tony

2. 長字串的跨行串接

對於較長的字串, 為了可讀性可以用 \ 串接延續到下一行, 例如 :

>>> 'This is \
... a book. That is\
... a pen.'
'This is a book. That isa pen.'

注意, 這裡的倒斜線 \ 只是單純的跨行串接符號而已, 沒有跳行作用, 跳行須自行加入 \n 字元. 例如 :

>>>a='This is \
... a pen.\nThat is\
... a book.'
>>> a
'This is a pen.\nThat isa book.'
>>> print(a)
This is a pen.
That isa book.

除了用行尾倒斜線跨行串接長字串外, Python 還可以用三個連續引號來表示跨行的長字串 (單引號或雙引號均可), 不過連續引號並非只能在跨行時用, 單行也可以用. 例如: 

>>> a='''Hello
...  World!'''
>>> a
'Hello\n World!'
>>> print(a)
Hello
 World!
>>> '''Hello World!'''
'Hello World!'

注意, 與上面用 \ 做跨行串接長字串不同的是, 使用三個連續引號時會保存輸入的格式, 這對處理像 JSON 或 HTML 那樣的長字串很方便. 例如上面 Hello 後是跳行, 這個 \n 字元會被認為是字串的一部分; 而上面用 \ 跨行串接長字串則只是單純串接, 並非跳行, 要跳行需自行加入 \n.   

3. 字串的長度 : 

由於字串繼承抽象型別 sized, 因此可以使用內建函數 len() 取得字串長度 (即 unicode 字元數) :

 函數 說明
 len(obj) 傳回物件元素個數

注意, 跳脫 (轉義) 字元雖然看起來由兩個字元組成, 但實際上算是一個字元. 例如 :

>>> len('Hello')
5
>>> len('\n')
1

4. 字串的基本操作 :

字串的基本操作有三種 :
  1. 串接
  2. 複製
  3. 切片 (slicing)
兩個字串常數 (字面值) 可以一前一後直接串接, 也可以用 + 運算子串接, 例如 :

>>> a='abc' '123'   #字串常數可以前後直接串接
>>> a
'abc123'
>>> a='abc' + '123' #用 + 運算子串接
>>> a
'abc123'

但是如果有一個是變數的話就必須使用 + 運算子串接, 例如 :

 >>> a='abc123'
>>> a '456'           #字串變數不可直接串接
Traceback (most recent call last):
  File "<stdin>", line 1
SyntaxError: invalid syntax
>>> a + '456'
'abc123456'

運算子 + 雖然身兼加號與自串串接兩種功能, 但 Python 是強型別語言, 不會像 Javascript 那樣進行隱藏的自動轉型, 必須強制轉型才可以, 例如 :

>>> '123' + 123          #Python 不會自動轉型
Traceback (most recent call last):
  File "<stdin>", line 1, in TypeError: unsupported types for __add__: 'str', 'int'
>>> int('123') + 123    #必須強制轉型
246

字串的複製使用 * 運算子, 例如 :

>>> 'abc123' * 3                #複製三次
'abc123abc123abc123'
>>> 'abc123\n' * 3             #含跳行字元複製三次
'abc123\nabc123\nabc123\n'
>>> print('abc123\n' * 3)
abc123
abc123
abc123

字串繼承自抽象型別 sequecnce, 由有序的 unicode 字元組成, 所以字串其實可以看做是不可變的字元串列. 雖然其內容為不可變, 但是可以利用中括號 [] 運算子擷取其元素 (字元), 或用切片 [start:end:step] 運算子擷取裡面的子字串成為新字串. 


所謂切片 (slicing) 是指使用中括號運算符 [] 以索引從字串中擷取子字串, 此用法是源自 Python 的序列 (Sequence) 抽象型別, 而 str, list, tuple 這三種型別都繼承自 Sequence 型別, 其每一個元素都可以使用索引操作之故. 切片的存取方式不僅用在字串, 也用在所有繼承 Sequence 型別之物件如串列與元組等.

索引值由左至右從 0 起算, 0 代表字串的開始字元; 若由字串尾端 (由右往左) 的話, 則從 -1 起算, 即最後一個字元索引為 -1, 倒數第二字原為 -2 等等. 但要注意索引若超出界線將產生錯誤, 例如 :

>>> a='0123456789'
>>> a[0]
'0'
>>> a[-1]
'9'
>>> a[9], a[-1]           #a[9] 與 a[-1] 是同一個字元
('9', '9')
>>> a[0], a[-10]         #a[0] 與 a[-10] 是同一個字元
('0', '0')
>>> a[10]                   #索引超出界限產生錯誤
Traceback (most recent call last):
  File "<stdin>", line 1
SyntaxError: invalid syntax

Python 的字串是不可變資料, 因此利用索引去更改字串內容將引發錯誤, 例如 :

>>> a='0123456789'
>>> a[0]
'0'
>>> a[0]='a'                 #字串內容不允許變更
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

使用切片從字串中取得子字串的語法為 [start:end:step], 其中 start 為開始索引, end 為結束索引, step 為步階 (即跳幾格取得下一個字元), 這三個索引以及冒號都是可以省略的, 但不能全部省略, 至少必須有一個冒號.

  1. [:] 會擷取整個字串
  2. [start:] 會擷取索引 start 到字串結尾
  3. [:end] 會擷取字串開頭至索引 end-1 (不包含索引 end)
  4. [start:end] 會擷取索引 start 至索引 end-1 (不包含索引 end)
  5. [start:end:step] 會擷取索引 start 至索引 end-1 (不包含索引 end), 跳 step 次
但是 MicroPython 不支援 step 大於 1 的步階.

例如 :

>>> a='0123456789'
>>> a[:]         #擷取整個字串
'0123456789'
>>> a[5:]       #擷取索引 5 到字串尾
'56789'
>>> a[:5]       #擷取字串首至索引 4
'01234'
>>> a[3:8]     #擷取索引 3 到索引 7
'34567'
>>> a[3:8:2]  #MicroPython 不支援步階大於 1
Traceback (most recent call last):
  File "<stdin>", line 1, in NotImplementedError: only slices with step=1 (aka None) are supported
>>> a[3:8:1]   #與 [3:8] 效果相同
'34567'

步階 step 大於 1 在 CPython 執行結果如下 :


C:\Users\Tony>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a='0123456789'
>>> a[3:8:2]    #索引 3~7, 跳 2 格
'357'

注意, Python 的切片功能在索引的使用上不會檢查是否超限, 即使超過也不會產生錯誤, 比使用單一索引容忍度高, 例如 :

>>> a='0123456789'
>>> a[:20]       #結束索引 20 已超限, 但不會出現錯誤
'0123456789'
>>> a[-20:]      #結束索引 -20 已超限, 但不會出現錯誤
'0123456789'

切片也可以使用負數索引, 表示從字串尾 (索引 -1) 起算, 例如 :

>>> a='0123456789'
>>> a[-3:]                   #a[-3:] 即 a[7:]
'789'
>>> a[:-3]                   #a[:-3] 即 a[:7]
'0123456'
>>> a[-6:-3]                #a[-6:-3] 即 a[4:7
'456'

間隔 step 也可以是負數, 表示由後往前 (或由右往左) 跳躍, 但是在 MicroPython 卻未實作負的間隔功能, 例如 :

>>> a='0123456789'
>>> a[::-1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NotImplementedError: only slices with step=1 (aka None) are supported
>>>

這個 [::-1] 是指整個字串 [::] 從後面每次取一個 , 在 CPython 執行結果如下 :

C:\Users\Tony>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)]
 on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a='0123456789'
>>> a[::-1]       #間隔 -1 表示從右到左每次取一個
'9876543210'


5. 字串物件的方法 :

除了上面三種基本的字串操作外, Python 的字串物件提供了豐富的方法來處理字串, 但須注意, Python 的字串是不可變 (immutable) 資料類型, 因此這些字串物件方法的傳回值如果是字串, 它都是傳回一個新字串, 並非改變了原來的字串.

(1). 大小寫操作

 方法 說明
 capitalize() 將首字母大寫, 其餘小寫 (未實作)
 lower() 將全部字元轉成小寫
 upper() 將全部字元轉成大寫
 swapcase() 將字元大寫變小寫, 小寫變大寫  (未實作)
 title() 將每個單字的首字元大寫, 其餘小寫   (未實作)

這五個方法在 MicroPython 只時做了 lower() 與 upper(), 例如 :

>>> a='This is a book. That is a pen.'
>>> a
'This is a book. That is a pen.'
>>> a.capitalize()
Traceback (most recent call last):
ppributeError: 'str' object has no attribute 'capitalize'
>>> a.lower()
'this is a book. that is a pen.'
>>> a                          #原字串不變
'This is a book. That is a pen.'
>>> a.upper()
'THIS IS A BOOK. THAT IS A PEN.'
>>> a.swapcase()
Traceback (most recent call last):
  File "AttributeError: 'str' object has no attribute 'swapcase'
>>> a.title()
Traceback (most recent call last):
  File "AttributeError: 'str' object has no attribute 'title'


(2). 字串內容檢查 :

 方法 說明
 islower() 字串中的英文字母都是小寫傳回真, 否則傳回假
 isupper() 字串中的英文字母都是大寫傳回真, 否則傳回假
 istitle() 字串中每個單字的首字元大寫其餘小寫傳回真, 否則傳回假   (未實作)
 isspace() 字串只含有空白字元 ('\t','\v','\n','\r','\f',' ') 傳回真, 否則傳回假
 isalpha() 字串只含有英文字母傳回真, 否則傳回假
 isdigit() 字串只含有數字 (0~9) 傳回真, 否則傳回假
 isdecimal() 字串只含有 10 進位數字傳回真, 否則傳回假 (未實作)
 isalnum() 字串只含有英文字母與數字傳回真, 否則傳回假 (未實作)
 isidentifier() 字串是否為合法的識別字 (未實作)
 isprintable() 字串只含有可見字元 (未實作)

這十個方法在 MicroPython 只實作了 5 個, istitle(), isdecimal(), isalnum(), isidentifier(), 以及 isprintable() 這 5 個未實作. 例如 :

>>> 'abc123'.islower()
True
>>> 'Abc123'.islower()
False
>>> 'Hello World'.istitle()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'istitle'
>>> '\t\v\n\r\f '.isspace()      #最後一個字元是空格
True
>>> '\t\v\n\r\f \a'.isspace()        #響鈴字元 '\a' 不是空白 (white space)
False
>>> 'abcxyzABCXYZ'.isalpha()
True
>>> 'abcxyzABCXYZ123'.isalpha()
False
>>> '0123456789'.isdigit()
True
>>> '0123456789abc'.isdigit()
False
>>> '0123456789'.isdecimal()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'isdecimal'
>>> '0123456789abcABC'.isalnum()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'isalnum'
>>> 'str'.isidentifier()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'isidentifier'
>>> 'abc123'.isprintable()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'isprintable'

(3). 尋找子字串 :

 方法 說明
 find(sub [,start [, end]]) 尋找子字串 sub, 找到傳回第一組之起始索引, 否則傳回 -1
 rfind(sub [,start [, end]]) 尋找子字串 sub, 找到傳回最後一組之起始索引, 否則傳回 -1
 index(sub [,start [, end]]) 與 find 相同, 但找不到子字串時會產生 valueError 錯誤
 rindex(sub [,start [, end]]) 與 rfind 相同, 但找不到子字串時會產生 valueError 錯誤
 count(sub [,start [, end]]) 在字串中尋找子字串 sub, 傳回子字串出現的次數
 startswith(pfx [,start [, end]]) 若字串以子字串 pfx 開頭傳回真, 否則傳回假 
 endswith(sfx [,start [, end]]) 若字串以子字串 sfx 結尾傳回真, 否則傳回假

上面七個方法都有備選的第二與第三參數, 不過在 MicroPython 中, startswith() 與 endswith()  只實作了一個參數的用法, 無法指定切片範圍去尋找. 其次, index()/rindex() 功能與 find()/rfind() 一樣, 差別是沒找到時, index()/rindex() 會傳回 -1, 而 find()/rfind() 則是產生 ValueError 錯誤, 例如 :

>>> '0123456789'.find('345')         #在索引 3 找到 '345'
3
>>> '0123456789'.find('345',2)       #從索引 2 開始找 (有找到)
3
>>> '0123456789'.find('345',4)       #從索引 4 開始找 (沒找到)
-1
>>> '0123456789'.find('345',2,5)     #切片 [2:5] 不包括索引 5 故沒找到
-1
>>> '0123456789'.find('345',2,6)     #切片 [2:6] 有包括索引 5 故有找到
3
>>> '0123456345'.find('345')         #有兩組匹配, 但只傳回第一組開始索引
3
>>> '0123456789'.rfind('345')        #第 1 組即最後一組
3
>>> '0123456345'.rfind('345')        #有兩組匹配, 但只傳回最後一組開始索引 7
7
>>> '0123456345'.rfind('345',2)      #從索引 2 開始找
7
>>> '0123456345'.rfind('345',2,7)    #在切片 [2:7] 中只找到第一組
3
>>> '0123456345'.rfind('345',2,5)    #在切片 [2:5] 中找不到傳回 -1
-1
>>> '0123456789'.index('345')        #在索引 3 找到 '345'
3
>>> '0123456789'.index('345',2,7)    #切片 [2:7] 有包括索引 5 故有找到
3
>>> '0123456789'.index('345',4,7)    #切片 [4:7] 不包括索引 3 故沒找到 (傳回錯誤)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: substring not found
>>> '0123456345'.index('345')        #與 find() 一樣只傳回第一組開始索引
3
>>> '0123456789'.rindex('345')       #在索引 3 找到 '345'
3
>>> '0123456789'.rindex('345',2)     #從索引 2 開始找 (有找到)
3
>>> '0123456789'.rindex('345',4,7)   #切片 [4:7] 不包括索引 3 故沒找到 (傳回錯誤)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: substring not found
>>> '0123456345'.rindex('345')       #與 rfind() 一樣只傳回最後一組開始索引
7
>>> '0123456789'.count('345')        #找到一組
1
>>> '0123456345'.count('345')        #找到兩組
2
>>> '0123456789'.count('345',2)      #從索引 2 開始找 (找到 1 組)
1
>>> ''0123456789'.count('345',4,7)   #切片 [4:7] 不包括索引 3 故沒找到
0
>>> '0123456789'.startswith('0123')
True
>>> '0123456789'.startswith('345',3)
True
>>> '0123456789'.startswith('345',2)
False
>>> '0123456789'.startswith('345',3,6)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function expected at most 3 arguments, got 4    #奇怪的錯誤訊息
>>> '0123456789'.endswith('789')
True
>>> '0123456789'.endswith('789',5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NotImplementedError: start/end indices                #endswith() 未實作多參數
>>> '0123456789'.endswith('789',5,9)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function expected at most 3 arguments, got 4    #奇怪的錯誤訊息

(4). 字串取代 :

 方法 說明
 replace(old, new [, count]]) 將子字串 old 全部用 new 替換, 或指定替換 count 個

注意, 這個方法會將字串中符合的子字串全部替換掉.

例如 :

>>> 'abc123abc123'.replace('abc','xyz')    #將字串中的 'abc' 以 'xyz' 取代
'xyz123xyz123'
>>> 'abc123abc123'.replace('abc','xyz',1)  #限制取代次數 1 次
'xyz123abc123'
>>> 'abc123abc123'.replace('abc','xyz',0)  #取代次數 0 表示不取代
'abc123abc123'
>>> 'abc123abc123'.replace('abc','xyz',3)  #取代次數超過沒作用
'xyz123xyz123'

(5). 字串去除 :

 方法 說明
 strip([chars]) 去除字串首尾的指定字元集合 chars, 預設刪除空白 
 lstrip([chars]) 去除字串開頭的指定字元集合 chars, 預設刪除空白 
 rstrip([chars]) 去除字串結尾的指定字元集合 chars, 預設刪除空白

注意, 若傳入參數指定欲刪除的字元, 預設刪除六種空白字元 (white space) :  \t\v\n\r\f 與空格.

例如 :

>>> ' abc '.strip()        #預設刪除 white space (\t\v\n\r\f 與空格)
'abc'
>>> 'abc123'.strip('a3')   #刪除首尾的 'a' 或 '3'
'bc12'
>>> 'abc123'.strip('c')    #刪除首尾的 'c' (沒有)
'abc123'
>>> 'abc123'.strip('32ba') #刪除首尾的 '3' 或 '2' 或 'b' 或 'a'
'c1'
>>> '\t\v\n\r\f abc\n123 \t\v\n\r\f'.strip()   #刪除首尾的 white space
'abc\n123'

(6). 字串填充 :

 方法 說明
 center(width [,fillchar]) 將字串居中擴展為 width 個字元, 左右以 fillchar 填充, 預設為空格 (未實作)
 ljust(width [,fillchar]) 將字串靠左擴展為 width 個字元, 右方以 fillchar 填充, 預設為空格 (未實作)
 rjust(width [,fillchar]) 將字串靠右擴展為 width 個字元, 左方以 fillchar 填充, 預設為空格 (未實作)
 zfill(width) 在字串左方補 0 使總長度擴展為 width 字元  (未實作)
 expandtabs([tabsize]) 將字串中的 tab 字元以 tabsize 個空格取代, 預設 8 格  (未實作)

字串填充功能在 MicroPython 全部未實作. 例如 :

>>> 'abc'.center(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'center'
>>> 'abc'.ljust(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'ljust'
>>> 'abc'.rjust(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'rjust'
>>> 'abc'.zfill(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'zfill'
>>> 'a\tbc'.expandtabs(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'expandtabs'

這五個方法在 CPython 執行結果如下 :

C:\Users\Tony>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> 'abc'.center(10)             #欄寬 10 格, 前後補空格
'   abc    '
>>> 'abc'.ljust(10)                #欄寬 10 格, 後面補空格
'abc       '
>>> 'abc'.rjust(10)                #欄寬 10 格, 前面補空格
'       abc'
>>> 'abc'.zfill(10)                 #欄寬 10 格, 前面補 0
'0000000abc'
>>> 'a\tbc'.expandtabs(10)    #實際 9 個空格
'a         bc'
>>> 'a\tbc'.expandtabs(3)      #實際 2 個空格
'a  bc'
>>> 'a\tbc'.expandtabs(2)      #實際 1 個空格
'a bc'
>>> 'a\tbc'.expandtabs(1)      #實際 1 個空格
'a bc'

觀察 expandtabs() 傳入不同參數結果, 發現傳入 1 與 2 實際空格為 1,  傳入 3 以上實際空格為 tabsize-1, 與我預期者不同, 不知作何解釋.

(7). 字串切割 :

 方法 說明
 partition(sep) 以 sep 為界由左至右分割字串為三部分, 傳回 sep 居中的 tuple (未實作)
 rpartition(sep) 以 sep 為界由右至左分割字串為三部分, 傳回 sep 居中的 tuple (未實作)
 split(sep [,maxsplit]) 以 sep 為界由左至右分割字串 maxsplit 次, 結果以 list 傳回
 rsplit(sep [,maxsplit]) 以 sep 為界由右至左分割字串 maxsplit 次, 結果以 list 傳回
 splitlines([keepends]) 以 \n 為界分割字串, 預設不保留跳行字元 (keepends=False) (未實作)

這五個方法在 MicroPython 僅實作了 split() 與 rsplit() 這兩個, 例如 :

>>> a='123,456,789'
>>> a.partition(',')                #未實作 partition()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'partition'
>>> a.rpartition(',')               #未實作 rpartition()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'rpartition'
>>> a.split(',')        #以 ',' 分拆整個字串, 拆開後以串列傳回
['123', '456', '789']
>>> a.split(',',2)     #指定最多拆 2 次
['123', '456', '789']
>>> a.split(',',1)     #指定最多拆 1 次
['123', '456,789']
>>> a.rsplit(',')       #從右邊往左拆
['123', '456', '789']
>>> a.rsplit(',',1)    #指定最多拆 1 次
['123,456', '789']
>>> a='123\n456\n789\n'     #含有跳行字元的字串
>>> print(a)
123
456
789

>>> a.splitlines()     #未實作 splitlines()
Traceback (most recent call last):
  File "AttributeError: 'str' object has no attribute 'splitlines'

如果有連續兩個分割字串, 則會分割出空字串, 例如 :

>>> a='123,,456,,789'    #連續的分割字串 ,
>>> a.split(',')
['123', '', '456', '', '789']

上面三個 MicroPython 未實作的方法在 CPython 執行結果如下 :

C:\Users\Tony>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a='123,456,789'
>>> a='123,456,789'
>>> a.partition(',')       #以第一個 ',' 為界拆成 3 份
('123', ',', '456,789')
>>> a.rpartition(',')      #以最後一個 ',' 為界拆成 3 份
('123,456', ',', '789')
>>> a='123\n456\n789\n'
>>> a.splitlines()          #預設不保留跳行字元
['123', '456', '789']
>>> a.splitlines(True)   #保留跳行字元
['123\n', '456\n', '789\n']

(8). 字串組合 :

 方法 說明
 join(iterable) 以字串為分隔字元串接 iterable 物件各元素為新字串後傳回

此 join() 函數使用上較特別, 是以分隔字元當操作母體, 以要串接的可迭代物件 (如串列, 元組等) 當參數, 例如 :

>>> a=['123','456','789']    #要組合的對象為串列元素
>>> ''.join(a)     #以空字串串接
'123456789'
>>> ' '.join(a)    #以空格串接
'123 456 789'
>>> '-'.join(a)    #以 '-' 串接
'123-456-789'
>>> ','.join(a)    #以 ',' 串接
'123,456,789'
>>> a=('123','456','789')    #要組合的對象為元組元素
>>> ''.join(a)     #以空字串串接
'123456789'
>>> ' '.join(a)    #以空格串接
'123 456 789'
>>> '-'.join(a)    #以 '-' 串接
'123-456-789'
>>> ','.join(a)     #以 ',' 串接
'123,456,789'

(9). 字串格式化 : 

Python 的字串最早是採用像 C 語言那樣以 % 運算子來格式化 (這也是餘數運算子), 較新的方式是使用 format() 函數. % 運算子搭配格式指定子後嵌入字串中, 就可以利用字串後面以 % 帶領的物件變數或 tuple 變數將值映射到對應的位置, 其組成方式如下 :

%[flag][width][.precision][specifier]

中括號表示每個組成成分都是可有可無的. 其中 [flag] (旗標) 有五個字元可用 :

 旗標 說明
 '#' 顯示另一種格式, 例如 16 進制的 ff 改為 0xff
 '0' 數值前面補 '0'
 '-' 靠左對齊 (若與 '0' 旗標同時出現會蓋掉欲補之 '0')
 ' ' 空格旗標 : 在正數前面補一個空格
 '+' 前面補 '+' (若與空格旗標同時出現會蓋掉欲補之空格)

[width] 用來設定要顯示的資料欄寬 (即字元個數); [.precision] 用來設定浮點數的精確度, 以一個小數點後面帶一個整數, 表示要精確到小數點後幾位 (CPython 預設 6 位); 而 [specifier] 用來設定資料要用何種格式輸出, 共有 15 種格式如下表 :

 格式 說明
 'd' 有號 10 進位整數
 'i' 有號 10 進位整數 (同 'd')
 'o' 有號 8 進位整數
 'x' 有號 16 進位整數 (小寫)
 'X' 有號 16 進位整數 (大寫)
 'f' 小數格式的浮點數
 'F' 小數格式的浮點數
 'e' 指數格式的浮點數
 'E' 指數格式的浮點數
 'g' 自動變換格式的浮點數, 指數 < -4 用指數表示 (小寫)
 'G' 自動變換格式的浮點數, 指數 < -4 用指數表示 (大寫)
 'c' 顯示一個字元 
 's' 顯示一個字串
 'r' 顯示一個用 repr() 轉換的字串
 '%' 顯示字元 '%'

其中 'd' 與 'i' 是完全一樣的功能. 例如 :

>>> '%d' %1234567890    #10 進位整數
'1234567890'
>>> '%i' %1234567890     #與 %i 相同
'1234567890'
>>> print("Hello %s%'Tony')     #字串
Hello Tony
>>> '%d %i %o %x %X' %(255, 255, 255, 255, 255)    #10/8/16 進位整數
'255 255 377 ff FF'
>>> '%f %F %e %E' %(0.123456789, 0.123456789, 0.123456789, 0.123456789)  #浮點數
'0.123457 0.123457 1.234568e-01 1.234568E-01'
>>> '%g %G' %(1.2345678e-4, 1.23456789e-4)    #浮點數 (自動判斷格式)
'0.000123457 0.000123457'
>>> '%g %G' %(1.2345678e-5, 1.23456789e-5)    #浮點數 (自動判斷格式)
'1.23457e-05 1.23457E-05'
>>> '%c %c' %(65, 'A')                #字元也可用整數
'A A'
>>> '%c %c %r' %(65, 'A', 'A')    #%r 會加上引號
"A A 'A'"

Python 的物件都可以轉換成字串, %s 格式也可以用在 list 與 tuple 等物件, 例如 :

>>> '%s %s' %([1,2,3], (4,5,6))    #list 與 tuple 被轉成字串輸出了
'[1, 2, 3] (4, 5, 6)'

旗標 '#' 用來以另類 (alterbative) 格式顯示資料, 例如在顯示 16 進位數值時會冠上 '0x' 或 '0X'; 顯示 8 進位數值時會冠上 '0o' :

>>> '%#x %#X %#o' %(255, 255, 255)
'0xff 0XFF 0o377'
>>> '%x %X %o' %(255, 255, 255)
'ff FF 377'

旗標 '0' 會在數值資料前面補 0 (不可用字串), 例如 :

>>> '%05d' %123         #欄寬 5, 前面補 0
'00123'
>>> '%05d' %123456   #超過欄寬當然不補 0
'123456'
>>> '%05x' %0xabc      #16 進制整數
'00abc'
>>> '%05d' %34.567     #小數會被捨去
'00034'

旗標 '-' 會將資料向左對齊, 與旗標 '0' 同時使用時, 在 CPython  是旗標 '-' 優先, 旗標 '0' 無作用, 但在 MicroPython 卻是向左靠齊後又在後面補 0, 例如 :

>>> '%-5d' %123       #向左靠齊
'123  '
>>> '%-05d' %123     #向左靠齊後右邊補 0
'12300'
>>> '%0-5d' %123     #向左靠齊後右邊補 0
'12300'
>>> '%0-5d' %123456
'123456'

而在 CPython 執行結果如下 :

C:\Users\Tony>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> '%0-5d' %123     #旗標 '-' 優先, 即向左靠齊不補 0
'123  '
>>> '%-05d' %123     #旗標 '-' 優先, 即向左靠齊不補 0
'123  '
>>> '%-05d' %123456
'123456'

旗標 '+' 則會在數值前面冠上正負號, 例如 :

>>> '%5d' %123
'  123'
>>> '%+5d' %123      #強制冠正負號
' +123'
>>> '%+5d' %-123     #強制冠正負號
' -123'
>>> '%5d' %-123       #負數不用旗標 '+' 本來就會有負數
' -123'

除了用 % 運算子做字串格式化外, Python 的 str 物件還提供了比 % 功能更強大的格式化函數 format(), 此函數也是建立在 % 運算子基礎上, 其置換欄位採用大括號 {}, 裡面可用 0 起始的索引指定套用的參數索引, 如果沒有指定索引, 就依據參數出現順序一一套用, 如果要顯示大括號本身, 就用 {{}}, 例如 :

>>> '{0} {1} {2}'.format(123, 4.567, 'abc')    #依指定之索引順序套用參數
'123 4.567 abc'
>>> '{} {} {}'.format(123, 4.567, 'abc')          #依參數出現順序套用
'123 4.567 abc'
>>> '{2} {0} {1}'.format(123, 4.567, 'abc')     #依指定之索引套用參數
'abc 123 4.567'

大括號內除了使用索引來定位要套用的參數外, 也可以像字典那樣使用 key, 這樣有 key 的參數在 format() 內可以放在任何位置, 但無 key 參數一定要放在對應位置, 例如 :

>>> '{} {name} {greetings}'.format('Hello!', name='Tony', greetings='You are welcome!')
>>> '{} {name} {greetings}'.format('Hello!', greetings='You are welcome!', name='Tony')
'Hello! Tony You are welcome!'
>>> '{} {name} {greetings}'.format(greetings='You are welcome!', name='Tony', 'Hello!')
Traceback (most recent call last):
  File "SyntaxError: non-keyword arg after keyword arg
如果要印出的是 list 或 tuple 等序列性物件, 也可以在大括號的索引後使用中括號索引來擷取物件內的元素, 但是 MicroPython 還沒有實作這種用法, 例如 :

>>> name=['tony','peter']
>>> gender=('male','femal')
>>> '{0[1]} {1[1]}'.format(name, gender)   #印出 name[1] 與 gender[1] (未實作)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NotImplementedError: attributes not supported yet
>>> '{0} {1}'.format(name[1], gender[1])   #索引放在 format() 裡就 OK
'peter femal'

這在 CPython 執行結果如下 :

Microsoft Windows [版本 10.0.14393]
(c) 2016 Microsoft Corporation. 著作權所有,並保留一切權利。

C:\Users\Tony>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> name=['tony','peter']
>>> gender=('male','femal')
>>> '{0[1]} {1[1]}'.format(name, gender)   #在 {} 內使用序列物件索引
'peter femal'
>>> '{1[1]} {0[1]}'.format(name, gender)   #{} 索引順序調換
'femal peter'
>>> '{[1]} {[1]}'.format(name, gender)       #不指定 {} 索引就依對應關係
'peter femal'
>>> '{0[3]} {1[1]}'.format(name, gender)   #索引超出界限了
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

這裡 {} 內的第一個數字是 {} 的索引, 而 [] 內的則是物件元素的索引.


二. 串列 (list) :

Python 的串列是有序可變的物件集群 (Collections), 可以儲存任何資料型態, 包括串列本身. 其功能比傳統的陣列更強大, 因為傳統語言如 C 或 Java 的陣列只能儲存相同的資料型態. Python 的串列比較像 Java 的 ArrayList 類別. 串列與集合 (set) 不同之處是, 串列是有序的, 而集合是無序的, 且串列同一個元素可以出現一次以上, 而集合的元素則必須是唯一的.

1. 建立串列 :

建立串列的方法不只一種, 但主要是使用 [] 運算子, 例如 :

>>> a=[]                     #建立一個空的串列
>>> type(a)                #型態為 list
<class 'list'>
>>> a=[0,1,2,3,4,5]    #建立一個數值串列
>>> a
[0, 1, 2, 3, 4, 5]
>>> print([1.728, 3.14, 6.02e23])      #印出一個數值串列
[1.728, 3.14, 6.02e+23]
>>> a=['a', 'b', 'c']        #建立字串串列
>>> a
['a', 'b', 'c']
>>> a=[123, 3.14, "abc", True]          #建立異質元素的串列
>>> a
[123, 3.14, 'abc', True]

由於串列是一種容器型別, 其元素可以是任何資料型態, 包括串列本身, 例如 :

>>> a=[(1,2,3),[4,5,6],{'x':7,'y':8,'z':9},{'a','b','c'}]  #元素包括 tuple, list, dict, 與 set
>>> a
[(1, 2, 3), [4, 5, 6], {'x': 7, 'z': 9, 'y': 8}, {'b', 'c', 'a'}]

第二種建立串列的方法是使用內建函數 list(), 只能傳入一個元素當作參數 (tuple 或字串), 例如 :

>>> a=list()
>>> type(a)
>>> a=list((123, 3.14, "abc", True))
>>> a
[123, 3.14, 'abc', True]


內建函數 list() 也可以將其他容器型別資料轉成串列, 例如會將傳入的字串拆成字元串列 :

>>> a='abc123'
>>> list(a)
['a', 'b', 'c', '1', '2', '3']          #字串被轉成字元串列

傳入不可變 (immutable) 的元組 (tuple) 會將其轉成可變的串列 :

>>> a=(1,2,3)      #元組
>>> list(a)
[1, 2, 3]    #將元組變成串列

傳入字典物件則會傳回其 key 所組成的串列 :

>>> a={'x':1,'y':2,'z':3}   #字典
>>> list(a)
['x', 'z', 'y']    #傳回字典的鍵組成之串列

傳入無順序的集合也會轉成串列 :

>>> a={1,2,3}    #集合
>>> list(a)
[3, 1, 2]  

第三種建立串列的方法是使用串列物件的 append() 方法, 例如 :

>>> a=[]
>>> a.append(123)
>>> a.append(3.14)
>>> a.append('abc')
>>> a.append(True)
>>> a
[123, 3.14, 'abc', True]

另外, 有一些內建函數其傳回值是串列, 例如字串的 split() 函數 :

>>> date="2017-05-05"
>>> date.split('-')
['2017', '05', '05']


還有一些內建函數也常被用來建立串列, 例如範圍函數 range([start], stop[, step]), 它會傳回一個整數序列物件 (但不是 list, 而是型態為 range 的序列物件), 其中 start (預設為 0) 與 stop 分別是起訖整數, step 則是間隔整數 (預設為 1), 利用 list() 函數可以將其轉換成串列, 例如 :

>>> r=range(10)             #預設 start=0
>>> r
range(0, 10)
>>> type(r)                     #range() 會傳回 range 型別物件
<class 'range'>
>>> list(r)                       #將 range 轉成 list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(3,9))        #間隔預設為 1
[3, 4, 5, 6, 7, 8]
>>> list(range(0,10,2))   #間隔=2
[0, 2, 4, 6, 8]
>>> list(range(0,-10,-2)) #間隔為負數時, stop 須小於 start
[0, -2, -4, -6, -8]
>>> list(range(0))            #0:0 為空串列
[]
>>> list(range(1,0))         #1:0 為空串列
[]


2. 串列的基本操作 :

串列的基本操作有下面 7 種 :
  1. 串列內容更改
  2. 串接 (使用 + 或 += 運算子)
  3. 複製 (使用 * 運算子)
  4. 切片 (使用 [] 運算子)
  5. 刪除元素 (使用 del 指令)
  6. 查詢元素 (使用 in 與 not in 運算子)
  7. 多重指定
串列是可變資料型態, 其內容可以用指定運算子加以變更, 例如 :

>>> a=[1,2,3]
>>> b=a          #b 也指向 a 的參考 (兩個內容一樣)
>>> a[0]='a'    #更改 a[0] 元素
>>> a
['a', 2, 3]                         #a[0] 被更改了
>>> b
['a', 2, 3]                         #b[0] 也同時被改了 (相同實體)
>>> b[0]='b'    #更改 b[0]
>>>b
['b', 2, 3]
>>> a
['b', 2, 3]                          #a[0] 也同時被改了 (相同實體)

注意, 變數是指向物件實體的參考, b=a 表示 b 變數的參考也是指向  a, 所以兩者都指向同一實體, 改任何一個另一個也同時被改了.

多個串列可用 + 或 += 運算子串接為新的串列 (另一個方法是使用 extend), 例如 :

>>> [1,2,3] + ['a','b','c']
[1, 2, 3, 'a', 'b', 'c']               #產生新串列
>>> a=[1,2,3]
>>> b=['a','b','c']
>>> a += b           #即 a=a+b, a 指向串接後的新串列
>>> a
[1, 2, 3, 'a', 'b', 'c']  

一個串列要複製其元素可使用 * 運算子 :

>>> [1,2,3]*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]       #元素複製 3 次

但是 MicroPython 卻不支援複合運算 *= :

>>> a=[1,2,3]
>>> a *= 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported types for : 'list', 'int'

這在 CPython 執行結果如下 :

C:\Users\Tony>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a=[1,2,3]
>>> a *= 3
>>> a
[1, 2, 3, 1, 2, 3, 1, 2, 3]

串列繼承 sequence 型別, 其元素是按照順序排列的, 因此可以用索引來擷取串列的單一元素, 而用切片 (slice) 則可以擷取多個元素. 如同字串, 其索引由左至右為 0 起始的連續整數 (索引必須是整數, 不可以為浮點數), 由右至左為 -1 起始 (即倒數第一個元素索引為 -1), 索引超出界限會產生 IndexError, 例如 :

>>> a=[0,1,2,3,4,5]
>>> a[0]
0
>>> a[1]
1
>>> a[2]
2
>>> a[1.5]    #索引須為整數, 不可以為浮點數
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not float
>>> a[-1]
5
>>> a[-2]
4
>>> a[-3]
3
>>> a[6]     #索引超出界限
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> a[-6]
0
>>> a[-7]    #索引超出界限
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

與字串一樣, 可以用切片 (slice) 來擷取串列中的多個元素 (傳回子串列), 格式如下 :

[start:end:step] 會擷取索引 start 至索引 end-1 (不包含索引 end), 跳 step 次

例如 :

>>> a=[0,1,2,3,4,5]
>>> a[:]              #擷取整個串列
[0, 1, 2, 3, 4, 5]
>>> a[3:]            #索引 3 至結尾
[3, 4, 5]
>>> a[:3]            #開頭至索引 2 (不包含 end)
[0, 1, 2]
>>> a[2:4]          #索引 2 至 3 (不包含 end)
[2, 3]
>>> a[0:5:2]       #索引 0 至 5 間隔 2
[0, 2, 4]
>>> a[0:5:3]       #索引 0 至 5 間隔 3
[0, 3]

刪除串列中指定索引之元素要用 del 指令, 後面的元素索引會往前挪, 例如 :

>>> a=[0,1,2,3,4,5]
>>> del a[3]   #刪除 a[3]
>>> a
[0, 1, 2, 4, 5]
>>> a[3]          #a[3] 由後面的 4 遞補了
4
>>> del a[-1]   #負索引 : 刪除倒數第 1 個元素
>>> a
[0, 1, 2, 4]

注意,  del 既非內建函數, 亦非串列的方法, 它是 Python 指令 del, 不只可用來刪除串列元素, 也可用來刪除物件以釋放記憶體, 例如 :

>>> a=[1,2,3]
>>> a
[1, 2, 3]
>>> del a     #從記憶體中刪除 a 變數
>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined      #變數 a 的記憶體已經被釋放了

若要查詢串列中是否含有某元素可用 in 與 not in 運算子, 傳回值為 True/False, 語法如下 :

運算元 in 串列
運算元 not in 串列

例如 :

>>> 'a' in ['a','b','c']
True
>>> 'a' not in ['a','b','c']
False
>>> 'abc' not in ['a','b','c']
True
>>> 'abc' in ['a','b','c']
False

串列也常被用在多重指定 (又稱為開箱 unpacking), 但是等號兩邊變數與元素個數必須相同, 否則會產生錯誤, 例如 :

>>> x,y,z=['a','b','c']     #多重指定
>>> x
'a'
>>> y
'b'
>>> z
'c'
>>> w,x,y,z=['a','b','c']   #等號兩邊個數不一致
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 3 values to unpack
>>> x,y=['a','b','c']         #等號兩邊個數不一致
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)

上面提到的 range() 函數也可以用在多重指定賦值, 例如 :

>>> a,b,c=range(3)   #列舉 0,1,2 給 a,b,c
>>> a
0
>>> b
1
>>> c
2
>>> type(a)
<class 'int'>
>>> a,b,c=range(5)     #但不可超出所需要之列舉
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)

3. 用內建函數操作串列 :

下列的內建函數可用在串列的操作, 它們會傳回新的串列 : 

 函數 說明
 len(obj) 傳回物件元素個數
 min(obj) 傳回物件中數值最小的元素
 max(obj) 傳回物件中數值最大的元素
 sum(obj) 傳回物件所有元素之和 
 sorted(obj)  傳回由小到大排列之物件
 reversed(obj) 傳回與原串列元素順序相反之迭代物件
 next(iterator) 傳回迭代物件的下一個元素
 zip(obj1, obj2) 傳回兩個物件元素一對一配對 tuple 組成的串列

函數 len() 會傳回串列中元素的個數, 例如 : 

>>> len([1,2,3,4,5])
5
>>> len([123, 3.14, "abc", True])
4

函數 min(), max(), 與 sum() 是用在串列元素為數值的情況, 分別傳回元素的最小值, 最大值, 以及全部元素和 :

>>> min([1,2,3])
1
>>> max([1,2,3])
3
>>> sum([1,2,3])
6

不過 min() 與 max() 也可以用在元素全部為字串的情況, 這時是逐一比較字串中的 ASCII 碼 :

>>> max(['a','b','c'])     #'c' 的 ASCII 碼較大
'c'
>>> min(['a','b','c'])      #'a' 的 ASCII 碼較小
'a'
>>> max(['aa','aA','Aa','AA'])       #'aa' 的 ASCII 碼較小
'aa'
>>> min(['aa','aA','Aa','AA'])        #'AA' 的 ASCII 碼較小
'AA'

但是, 不論 min(), max(), 還是 sum() 都無法處理元素為數字與字串混雜的串列, 因為 Python 不知道要如何比較 :

>>> min([1,'b','c'])             #無法對數字與字串進行比較
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported types for __lt__: 'str', 'int'
>>> sum([1,'b',3])              #無法對數字與字串進行加總
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported types for __add__: 'int', 'str'

函數 sorted() 會將串列元素由小至大排序後傳回新串列, reversed() 則由大至小排序, 原串列不受影響 (不是 in-place 運算), 例如 :

>>> a=[5,1,0,3,2,4]
>>> sorted(a)  #由小至大排序
[0, 1, 2, 3, 4, 5]
>>> a            
[5, 1, 0, 3, 2, 4]               #原串列不受影響

sorted() 也可以對字串串列排序, 依據 ASCII 碼的由小至大先後排列, 因此數字會比字母優先 (因 0~9 之 ASCII 為 48~57), 大寫字母 (ASCII 為 65~90) 又比小寫字母 (ASCII 為 97~122) 優先, reversed() 則反過來, 不過 reversed() 函數傳回的不是串列, 而是一個迭代物件 (iterator), 可用 next() 函數取得下一個元素, 也可以用 list() 將其轉成串列, 例如 :

>>> a=['a','3','B','1','c','A','b','2','C']
>>> sorted(a)
['1', '2', '3', 'A', 'B', 'C', 'a', 'b', 'c']                 #數字先, 其次大寫, 最後是小寫
>>> a=['aA','aa','AA','Aa','BB','Bb','bB','bb']
>>> sorted(a)
['AA', 'Aa', 'BB', 'Bb', 'aA', 'aa', 'bB', 'bb']    #第一個字母先排序, 再排序第二個字母
>>> a
['aA', 'aa', 'AA', 'Aa', 'BB', 'Bb', 'bB', 'bb']    #原串列內容不便 (因不是 in-place)
>>> reversed(a)                             #reversed() 傳回的是 iterator 物件, 不是串列
>>> list(reversed(a))                     #用 list() 轉成串列
['bb', 'bB', 'Bb', 'BB', 'Aa', 'AA', 'aa', 'aA']    #順序跟原串列相反
>>> a
['aA', 'aa', 'AA', 'Aa', 'BB', 'Bb', 'bB', 'bb']    #原串列內容不便 (因不是 in-place)


next() 函數會從迭代物件 reversed 中取出下一個元素, 每取出一個裡面就少一個, 直到沒有可迭代之元素為止, 例如 :

>>> a=[1,2,3]
>>> r=reversed(a)
>>> r
<reversed>             #傳回迭代物件
>>> next(r)       #取出迭代元素
3
>>> next(r)
2
>>> next(r)
1
>>> next(r)      #迭代物件已空, 出現錯誤
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration:
>>> r
<reversed>             #迭代物件還在, 但內容已空
>>> list(r)         #用 list() 轉成串列, 是空串列
[]

但是要注意, 不要將含有字串與數字的串列傳給 sorted() 去排序, 因為 Python 不知道要採用甚麼準則去排序 :

>>> a=['a',3,'B',1,'c','A','b',2,'C']      #元素有數字也有字串
>>> sorted(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported types for __lt__: 'str', 'int'

但是傳給 reversed() 卻不會產生錯誤, 可以正確地將元素順序反轉

>>> a=['a',3,'B',1,'c','A','b',2,'C']    #混和字串與數字元素的串列
>>> r=reversed(a)                          #reversed() 不會出現錯誤
>>> r
<reversed>
>>> list(r)                                       #將迭代物件轉成串列
['C', 2, 'b', 'A', 'c', 1, 'B', 3, 'a']                        #順序與原串列相反

最後來測試 zip(), 此函數接受兩個元素個數相等的串列, zip() 會將相對應元素兩兩配成一個 tuple, 然後組成一個串列傳回, 例如 :

>>> a=[1,2,3]
>>> b=['a','b','c']
>>> c=zip(a,b)      #把對應元素 (1,'a') 等送作堆
>>> c
<zip>                           #傳回 zip 物件 (也是迭代物件)
>>> type(c)
<class 'zip'>
>>> list(c)             #將 zip 物件轉成串列
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> next(c)           #轉成串列會完成迭代, 迭代物件將變空
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration:
>>> c=zip(a,b)      #重新產生 zip 物件
>>> next(c)           #用 next() 迭代取出下一個迭代元素
(1, 'a')
>>> next(c)
(2, 'b')
>>> next(c)
(3, 'c')
>>> list(c)             #迭代 3 次就成空了
[]

3. 串列物件的方法 :

Python 的串列物件也提供了幾個方法來操作陣列 :

 方法 說明 
 append(obj) 將一個元素添加至串列尾端
 extend(seq) 將一個序列物件添加至串列尾端
 insert(index, obj) 將一個元素 obj 添加至串列中的指定索引 index 前面
 remove(obj) 從串列中刪除指定之元素
 pop(index) 取出並傳回指定索引之元素 (預設索引 -1)
 sort() 將串列元素排序 (由小到大)
 reverse() 將串列元素反向排序 (由大到小)
 count(obj) 傳回串列中出現 obj 元素的次數
 index(obj) 傳回元素 obj 在串列中的索引
 copy() 複製串列元素
 clear() 刪除串列中的全部元素

注意, 這些方法都是就地 (in-place) 運作的, 亦即這些方法是直接在串列上操作, 而非在複本中操作. 除了 pop() 外都沒沒有傳回值 (或者說傳回 None), 因此不要把傳回值設給原串列, 這樣會讓原串列變成 None.

例如 :

append() 的作用是將傳入的元素加入串列的尾端, 串列的元素可以是任何型態物件 :

>>> a=['a','b','c']
>>> a.append('d')      #在尾端加入 'd'
>>> a
['a', 'b', 'c', 'd']
>>> a.append([1,2])  #在尾端加入串列
>>> a
['a', 'b', 'c', 'd', [1, 2]]
>>> a.append((3,4))   #在尾端加入元組
>>> a
['a', 'b', 'c', [1, 2], (3, 4)]

extend() 用來串接兩個串列, a.extend(b) 是把 b 串列串接到 a 的尾端, 例如 :

>>> a=[1,2,3]
>>> b=['a','b','c']
>>> a.extend(b)     #b 串接到 a 後面
>>> a
[1, 2, 3, 'a', 'b', 'c']
>>> b
['a', 'b', 'c']
>>> b.extend(a)     #a 串接到 b 後面
>>> b
['a', 'b', 'c', 1, 2, 3, 'a', 'b', 'c']

串列串接也可以用 + 或 += 運算子, 例如 :

>>> a=[1,2,3]
>>> b=['a','b','c']
>>> a=a + b             #b 串接到 a 後面
>>> a
[1, 2, 3, 'a', 'b', 'c']
>>> b
['a', 'b', 'c']
>>> b += a               #a 串接到 b 後面
>>> b
['a', 'b', 'c', 1, 2, 3, 'a', 'b', 'c']

insert() 是在指定索引前面加入元素, 如果索引超出界線不會出現錯誤, 而是自動放在最後面. 索引也可以是負值, -1 是倒數第一個 :

>>> a=['a','b','c']
>>> a.insert(2,1)     #在索引 2 前面加入 1
>>> a                    
['a', 'b', 1, 'c']                           #加在原索引 2 元素 'c' 前面
>>> a.insert(6,'e')    #指定索引 6 超出界限, 會被放在最後面
>>> a
['a', 'b', 1, 'c', 'e']
>>> a.insert(-2,2)    #在倒數第 2 個 'c' 前插入 2
>>> a
['a', 'b', 1, 2, 'c', 'e']

remove() 則是從串列中刪除指定之元素, 若串列中沒有該元素會產生錯誤 :

>>> a=['a','b','c']
>>> a.remove('b')    #刪除元素 'b'
>>> a
['a', 'c']
>>> a.remove('d')     #刪除一個不存在的元素會產生錯誤
Traceback (most recent call last):
  File "<stdin>", line 1, in ValueError: object not in sequence


使用 remove() 方法跟上面使用 del 指令的差別是 : remove() 方法是刪除指定的元素, 必須知道元素內容是甚麼; 而 del 指令是刪除指定索引, 必須知道要刪除的元素索引是多少.

pop() 函數會從串列中取出指定索引之元素, 並從串列中刪除此元素後將其傳回, 這是串列方法中唯一有傳回值者. 如果沒有指定索引, 預設為 -1, 亦即取出最後一個元素傳回. 若索引超出界限將產生錯誤, 例如 :

>>> a=[0,1,2,3,4,5]
>>> a.pop()       #取出末端元素 5
5
>>> a
[0, 1, 2, 3, 4]
>>> a.pop(-1)    #取出末端元素 4
4
>>> a
[0, 1, 2, 3]
>>> a.pop(2)     #取出索引 2 元素 2
2
>>> a
[0, 1, 3]
>>> a.pop(5)     #取出引 5 元素 (超出界限)
Traceback (most recent call last):
  File "<stdin>", line 1, in IndexError: list index out of range

sort() 函數會將串列元素做升冪排序, 如果傳入參數 reverse=True 的話就會做降冪排序.


>>> a=[5,1,3,2,0,4]
>>> a.sort()
>>> a
[0, 1, 2, 3, 4, 5]
>>> a.sort(reverse=True)
>>> a
[5, 4, 3, 2, 1, 0]

如果串列的元素是字串, 則排序是依據 ASCII 碼做升冪排序, 因此數字會比字母優先 (因 0~9 之 ASCII 為 48~57), 大寫字母 (ASCII 為 65~90) 又比小寫字母 (ASCII 為 97~122) 優先, reversed() 則反過來, 

>>> a=['a','3','B','1','c','A','b','2','C']
>>> a.sort()
>>> a
['1', '2', '3', 'A', 'B', 'C', 'a', 'b', 'c']      #字串依據 ASCII 碼升冪排序

若傳入 reverse=True 則做降冪排序 :

>>> a=['a','3','B','1','c','A','b','2','C']
>>> a.sort(reverse=True)   #字串依據 ASCII 碼排序
>>> a
['c', 'b', 'a', 'C', 'B', 'A', '3', '2', '1']

與內建函數 sorted() 一樣, 不要對混和數字與字串的串列排序, 這樣會產生錯誤, 例如 :

>>> a=['a',3,'B',1,'c','A','b',2,'C']     #串列元素有數值也有字串
>>> a.sort()
Traceback (most recent call last):
  File ">stdin>", line 1, in >module>
TypeError: unsupported types for __lt__: 'str', 'int'

注意, 串列物件的 sort() 方法與內建函數 sorted() 最大的不同是, sort() 是就地 (in-place) 操作, 直接在串列物件上排序, 執行後串列元素的順序被改變, 沒有傳回值; 而 sorted() 函數是在一個副本上排序, 執行後傳回此副本的參考, 元串列不受影響.

count() 方法會傳回指定之元素在串列中出現的次數 (整數), 例如 :

>>> a=[1,2,3,3,3,2]
>>> a.count(1)
1
>>> a.count(2)
2
>>> a.count(3)
3
>>> a=['a','b','c',1,2,3,'a','b']
>>> a.count('a')
2
>>> a.count('b')
2
>>> a.count('c')
1
>>> a.count('d')    #無此元素
0

index() 方法會傳回傳入元素在串列中的索引, 例如 :

>>> a=[0,1,2,3,4,5]
>>> a.index(0)
0
>>> a.index(3)
3
>>> a.index(5)
5

copy() 方法會將串列複製一份副本, 並傳回此副本的參考. 這跟用切片的 [:] 效果是一樣的, 都是產生一個新串列, 例如 :

>>> a=[1,2,3]
>>> b=a.copy()   #傳回 a 串列副本的參考, 指定給串列 b
>>> b
[1, 2, 3]
>>> b == a          #用 == 比較兩者內容相同
True
>>> b is a            #用 is 比較兩者參考不同 (不同物件)
False
>>> c=a[:]           #用切片複製串列
>>> c == a          #用 == 比較 a,c 兩者內容相同
True
>>> c == b          #用 == 比較 b,c 兩者內容相同
True
>>> c is a            #用 is 比較 a,c 兩者參考不同 (不同物件)
False
>>> c is b            #用 is 比較 b,c 兩者參考不同 (不同物件)
False
>>> b[0]='b'        #更改 b 串列內容
>>> c[0]='c'        #更改 c 串列內容
>>> b             
['b', 2, 3]
>>> c
['c', 2, 3]
>>> a                   #串列 a,b,c 互不影響 
[1, 2, 3]

clear() 會清除串列內所有元素, 使其成為一個空字串, 例如 ;

>>> a=[1,2,3]
>>> a
[1, 2, 3]
>>> a.clear()
>>> a
[]                         #變成空串列

字串有 2 個方法與串列密切相關 : join() 與 split(), 兩者為反運算, join() 是將一個串列以一個膠合字串串接成一個新字串後傳回; 而 split() 則是以一個分拆字串為界, 將一個字串拆成數個子字串組成的串列後傳回, 例如 :

>>> a=['123','456','789']
>>> b='-'.join(a)        #以 '-' 串接
>>> b
'123-456-789'
>>> c=b.split('-',1)    #以 '-' 自左向右分拆 1 次
>>> c
['123', '456-789']
>>> c=b.split('-')       #以 '-' 自左向右全部分拆
>>> c
['123', '456', '789']
>>> c=b.rsplit('-')      #以 '-' 自右向左全部分拆
>>> c
['123', '456', '789']
>>> c=b.rsplit('-',1)   #以 '-' 自右向左分拆 1 次
>>> c
['123-456', '789']

三. 元組 (tuple) :

元組是串列的不可變版 (可視為常數串列), 其物件建立後內容即不可變更, 亦即, 在上述串列中會改變物件內容的指定運算不能使用, 而且物件方法也只有 index() 與 count() 兩個而已. 既然已經有串列這麼好用的型別了, 為何 Python 又要定義 tuple 型別呢? 理由如下 :
  1. tuple 比 list 占用的記憶體較少
  2. 在元素必須固定不可以被變更場合 tuple 最保險
  3. 函數的引數是以 tuple 形式傳入的
  4. tuple 很容易轉成字典 dict
  5. 具名元組 (named tupe) 可作為簡化之物件用
1. 建立元組 :

通常元組使用 () 運算子來標示, 元素之間用半形逗號隔開, 即使只有一個元素, 也必須在結尾加上逗號. 事實上, () 運算子是非必要的, Python 是用逗號來辨別這是一個 tuple, 例如 :

>>> name=('peter',)        #元組只有一個元素時結尾務必有逗號
>>> type(name)
<class 'tuple'>
>>> name='peter',          #即使不用 (), 只要有逗號就會被認為是 tuple
>>> type(name)
<class 'tuple'>
>>> name=('peter')         #沒有結尾逗號會被認為是字串
>>> name
'peter'
>>> type(name)
<class 'str'>

可見真正定義一個元組的是逗號而非小括弧 (), 用小括弧把元組的元素包起來只是讓我們更容易識別這是一個 tuple 而已 (當然若使用中括號就被視為串列, 大括弧表示集合或字典). 如果元組有多個元素, 則結尾的逗號可有可無 (因為已經至少有一個逗號足以識別為 tuple 了), 例如 :

>>> name='peter','amy','kelly'       #多於一個元素時結尾逗號可省略
>>> type(name)
<class 'tuple'>
>>> name
('peter', 'amy', 'kelly')
>>> name='peter','amy','kelly',       #結尾有逗號當然沒問題
>>> name
('peter', 'amy', 'kelly')
>>> type(name)
<class 'tuple'>
>>> name=('peter','amy','kelly')      #用小括弧宣揚其 tuple 身分
>>> name
('peter', 'amy', 'kelly')
>>> type(name)
<class 'tuple'>
>>> name=('peter','amy','kelly',)     #結尾有逗號當然沒問題
>>> name
('peter', 'amy', 'kelly')
>>> type(name)
<class 'tuple'>
>>>

元組乃是常數串列, 所以無法使用 append() 方法從空的元組建立 tuple.

第二種建立 tuple 的方法是使用內建函數 tuple(), 它會將其他型別物件如字串, 串列, 字典, 以及集合轉成元組, 例如 :

>>> tuple('abc')           #字串轉元組:每個字元會被打散成字元 tuple
('a', 'b', 'c')
>>> tuple(['a','b','c'])    #串列轉元組
('a', 'b', 'c')
>>> tuple({'a':1,'b':2,'c':3})   #字典轉元組
('b', 'c', 'a')
>>> tuple({'a','b','c'})    #集合轉元組
('b', 'c', 'a')


2. 元組的基本操作 :

由於元組是常數串列, 所以元組的可操作方式比串列少, 總之要更改 tuple 內容的操作是不允許的 :
  1. 串接 (使用 + 運算子)
  2. 複製 (使用 * 運算子)
  3. 切片 (使用 [] 運算子)
  4. 查詢元素 (使用 in 與 not in)
  5. 多重指定
與串列相同, tuple 可以使用 + 運算子串接與 * 運算子複製, 結果會傳回一個新 tuple, 例如 :

>>> a=(1,2,3,)
>>> b=(4,5,6,)
>>> c=a+b
>>> c
(1, 2, 3, 4, 5, 6)
>>> a
(1, 2, 3)
>d=a*3
>>> d
(1, 2, 3, 1, 2, 3, 1, 2, 3)

元組與串列一樣是繼承 sequence 之序列型別, 因此可以使用整數索引去擷取個元素, 例如 :

>>> a=0,1,2,3,4,5
>>> a[0]
0
>>> a[3]
3
>>> a[5]
5

但是元組屬於不可變型別 (immutable), 所以物件建立後無法再用指定運算子更改其內容, 例如 :

>>> a=(0,1,2,3,4,5)
>>> a[0]='a'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

元組也可以利用索引擷取 tuple 中的切片 (slice), 例如 :

>>> a=0,1,2,3,4,5,
>>> a
(0, 1, 2, 3, 4, 5)
>>> a[:]         #擷取全部元素
(0, 1, 2, 3, 4, 5)
>>> a[3:]       #擷取索引 3 至結尾的元素
(3, 4, 5)
>>> a[:3]       #擷取開頭至索引 2 (不包括結束索引 3)
(0, 1, 2)
>>> a[2:4]     #擷取索引 2 至 3 (不包括結束索引 4)
(2, 3)
>>> a[0:5:1]  #擷取索引 0 至索引 5 步階 1 (不包括結束索引 5)
(0, 1, 2, 3, 4)
>>> a[0:5:2]  #MicroPython 不支援步階大於 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NotImplementedError: only slices with step=1 (aka None) are supported

注意, 上面串列的測試顯示, 串列的切片步階可大於 1, 但在元組卻沒有, MicroPython 的元組在切片的步階 (step) 參數上只支援 step=1, 不支援大於 1 的步階, 這在 CPython 執行結果為 :

C:\Users\cht>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)]
 on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a=0,1,2,3,4,5
>>> a[0:5:2]
(0, 2, 4)

要查詢指定元素有無在 tuple 中可用 in 與 not in 指令, 例如 :

>>> a=('a','b','c')
>>> 'a' in a
True
>>> 'a' not in a
False
>>> 'abc' not in a
True

與 list 一樣, tuple 也可以做多重指定 (又稱為開箱, unpacking), 例如 :

>>>x,y,z='a','b','c',        
>>> x
'a'
>>> y
'b'
>>> z
'c'
>>> w,x,y,z=('a','b','c')     #兩邊不一致
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 3 values to unpack
>>> y,z=('a','b','c')           #兩邊不一致
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)

3. 用內建函數操作元組 :

雖然元組是串列的不可變版, 但下列內建函數並非就地 (in-place) 操作, 不會去更改元組內容, 因此也可以用在元組 :

函數 說明
 len(obj) 傳回物件元素個數
 min(obj) 傳回物件中數值最小的元素
 max(obj) 傳回物件中數值最大的元素
 sum(obj) 傳回物件所有元素之和 
 sorted(obj)  傳回由小到大排列之物件
 reversed(obj) 傳回與原物件元素順序相反之迭代物件
 next(iterator) 傳回迭代物件的下一個元素
 zip(obj1, obj2) 傳回兩個物件元素一對一配對的 zip 物件

>>> len((1,2,3,4,5))
5
>>> len((123, 3.14, "abc", True))
4
>>>

函數 min(), max(), 與 sum() 會傳回元素為數值的元組中的最小值, 最大值, 以及加總值 :

>>> min((1,2,3))
1
>>> max((1,2,3))
3
>>> sum((1,2,3))
6

這三個函數也可以用在全部元素是字串情況 (依 ASCII 碼逐一比對), 但不能用在元素包含數值與字串混合情況 :

>>> max(('a','b','c'))
'c'
>>> min(('a','b','c'))
'a'
>>> max(('aa','aA','Aa','AA'))
'aa'
>>> min(('aa','aA','Aa','AA'))
'AA'
>>> min((1,'b','c'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported types for __lt__: 'str', 'int'
>>> sum((1,'b',3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported types for __add__: 'int', 'str'

函數 sorted() 會將元素升冪排序後以 list 傳回, 而 reversed() 則是將元素順序反向排序後傳回一個 reversed 物件 (迭代物件), 可用 next() 取出其迭代元素, 也可以用 list() 將其轉成串列 :

>>> a=(5,1,0,3,2,4)
>>> sorted(a)         #升冪排序, 傳回串列
[0, 1, 2, 3, 4, 5]
>>> a
(5, 1, 0, 3, 2, 4)
>>> reversed(a)     #反序排序, 傳回 reversed 物件
<reversed>
>>> list(reversed(a))    #將 reversed 物件轉成串列
[4, 2, 3, 0, 1, 5]
>>> b=reversed(a)       #反序排序, 傳回 reversed 物件
>>> next(b)                  #取出 reversed 物件元素
4
>>> next(b)
2
>>> next(b)
3
>>> next(b)
0
>>> next(b)
1
>>> next(b)
5
>>> next(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration:

而 zip() 函數可將傳入的兩個 tuple 物件對應元素配對後傳回 zip 物件 (迭代物件), 可以用 next() 函數檢視此 zip 物件之元素, 也可以用 tuple() 函數轉成元組, 或者用 list() 函數轉成串列 :

>>> a=1,2,3,
>>> b=4,5,6,
>>> zip(a,b)            #傳回 zip 物件
<zip>
>>> list(zip(a,b))     #轉成 list
[(1, 4), (2, 5), (3, 6)]
>>> tuple(zip(a,b))  #轉成 tuple
((1, 4), (2, 5), (3, 6))
>>> c=zip(a,b)      
>>> type(c)
<class 'zip'>
>>> next(c)               #取出下一個迭代元素
(1, 4)
>>> next(c)               #取出下一個迭代元素
(2, 5)
>>> next(c)               #取出下一個迭代元素
(3, 6)

4. 元組物件的方法 :

因為元組是不可變的串列, 所以串列物件的方法中只有 count() 的 index() 在元組中可用 :

 方法 說明 
 count(obj) 傳回串列中出現 obj 元素的次數
 index(obj) 傳回元素 obj 在串列中的索引

例如 :

>>> a=(1,2,3,3,3,2)
>>> a.count(1)
1
>>> a.count(2)
2
>>> a.count(3)
3
>>> a=('a','b','c',1,2,3,'a','b')
>>> a.count('a')
2
>>> a.count('b')
2
>>> a.count('c')
1
>>> a.count('d')
0
>>> a=[0,1,2,3,4,5]
>>> a.index(0)
0
>>> a.index(3)
3
>>> a.index(5)
5

以上便是 MicroPython 序列型別的完整測試, 還真長.

3 則留言:

  1. 小狐狸你好:
    此篇的大標題 二.串列,1.建立串列:
    當中的這段
    >>> a=list()
    >>> type(a)
    >>> a=list(123, 3.14, "abc", True)
    >>> a
    [123, 3.14, 'abc', True]

    我試著照做,但是出現錯誤訊息
    >>> a=list()
    >>> type(a)

    >>> a=list(123, 3.14, "abc", True)
    Traceback (most recent call last):
    File "", line 1, in
    a=list(123, 3.14, "abc", True)
    TypeError: list expected at most 1 argument, got 4

    在MicroPython跟Python都一樣出現這個ERROR,其中Python的2.7.17版跟3.8.0版皆出現此ERROR
    用google翻譯錯誤訊息是說參數最多應只有1個,找到4個
    想請問這個狀況該怎麼排除?

    回覆刪除
  2. 阿麥您好, 感謝您的留言, 抱歉, 我少打了第二層小括弧, 正確是這樣 :

    >>> a=list((123, 3.14, "abc", True))
    >>> a
    [123, 3.14, 'abc', True]

    您查的資訊是對的, list() 函數只接受一個參數, 那就是 tuple, 所以需要兩層小括號, 內層代表 tuple.

    回覆刪除
  3. 原來如此!感謝解惑。:)

    回覆刪除