2020年2月24日 星期一

Python 學習筆記 : 基本語法 (一) : 變數, 資料型態, 與運算子

用 Python 那麼久了, 筆記也寫了一些, 但卻一直沒有對基本語法做個完整測試與整理. 此篇自 2019 年 8 月卸下邏輯設計授課重擔後配合二哥學習 Python 開始整理, 但雜務太多斷斷續續, 利用 2020 舊曆年連假大致告一段落, 但仍不完整, 直到今天才完工.

其實之前在學 MicroPython 時也對 Python 基本語法做過測試, 但那時太倉促, 而且是在 ESP8266 物聯網嵌入式設備上進行, 而非在 CPython 上, 所以也不太完整, 參考 :

MicroPython on ESP8266 (二) : 數值型別測試
MicroPython on ESP8266 (三) : 序列型別測試
MicroPython on ESP8266 (四) : 字典與集合型別測試

CPython 核心語法包括下列 7 個部分 :
  1. 分行與併行
  2. 縮排
  3. 註解
  4. 變數與常數
  5. 資料型態
  6. 運算子
  7. 流程控制
  8. 函數
  9. 自訂函數
  10. 變數作用範圍
下面這個網站可找到豐富的 Python 程式範例 :

# Python Code Examples (Program Creek)

另外屬於 Python 程式寫作風格 (廣義的語法) 參考 :

PEP8 - Python 程式碼風格指引(中文)

其他不錯的網站參考 :

https://www.w3schools.com/python/default.asp

本篇測試是讀過下列書籍後整理的 :
  1. 深入淺出 Python 第二版 (歐萊禮)
  2. 一步到位 Python 程式設計 (旗標, 陳惠貞)
  3. 精通 Python-運用簡單的套件進行現代運算 (歐萊禮)
  4. Python 神乎其技 (旗標, 江良志譯)
這些書都各有所長, 但以第 2 本較有系統且完整.


一. 分行與併行 :

Python 程式的最小執行單位是敘述 (statement), 它可以是一個指派 (assignment), 運算式 (expression), 或函數呼叫 (function call). 一個敘述通常占一行, 行尾直接跳行不需要像 Java 與 C 那樣使用分號當結束標記, 但即使行尾加分號也不會有錯誤, 例如 :

>>> a='hello world'         
>>> print(a) 
hello world
>>> a='hello world'; 
>>> print(a); 
hello world

分號在 Python 中的作用是併行, 亦即多個敘述可寫在同一行但用分號隔開, 例如 :

>>> a='hello world'; print(a); 
hello world

若敘述太長, 可在敘述的任何位置用倒斜線 \ 斷開, 並立即按 enter 跳行至下一行接續, 將敘述分成多行, 例如 :

>>> msg='hello \
world! \
Tony'
>>> print(msg)
'hello world! Tony'

注意, 倒斜線後面不可以有空白, 必須立即跳行, 否則會出現 "SyntaxError: EOL while scanning string literal" 錯誤 (在 IDLE 介面會被自動消除不會報錯, 但在 .py 程式檔執行時會出錯). 對於串列, 元組, 集合, 字典這四個資料結構, 因為有括弧作為界線, 其元素或項目太多時可直接跳行不需要用 \ 串接 (用也 OK).

另外一種分行的方法是使用小括弧 (), 這樣就不需要倒斜線了, 例如 :

>>> msg=('hello ' + 
'world! ' + 
'Tony') 
>>> print(msg)   
'hello world! Tony'

將字串用 + 串接, 並且用小括弧包起來就不需要倒斜線, 也可以將整個敘述包起來, 例如 :

>>> (print('hello ' +
   'world! ' +
   'Tony'))
hello world! Tony


二. 縮排 :

Python 程式的特點是強制要求縮排 (indentation), 其唯一的程式區塊分隔符號為冒號, 冒號換行後開始縮排, 開始縮排標示著一個程式區塊之開始, 取消縮排則標示區塊結束. 縮排是 Python 語法的一部分, 違反縮排規則的程式將無法執行. 

每個程式區塊 (if, else, for, def, class, try, except 等) 內都必須有一致的縮排 (REP 建議是用 4 個空白為一個縮排單位), 但各區塊之縮排可以不同. 縮排可使程式碼格式整齊劃一, 提高可讀性與降低維護難度, 特別是在多人共同開發大型程式時, 更能彰顯強制縮排的優點.

if score >=60 :
    result='pass'
else :
    result='no pass'

注意, 編輯 Python 程式時最好是按 "Space" 鍵來建立縮排, 不要按 "Tab" 鍵.  如果編輯器有類似 "Insert spaces instead of Tab" 的設定選項建議勾選, 這樣按 "Tab" 鍵時會以所設定之空格 (space) 取代.
   

三. 註解 :

Python 有兩種註解符號 :
  1. 單行註解 :
    使用井字號 #, 此符號開始到該行結尾會被解譯器忽略不予執行. 
  2. 多行註解 :
    使用成對的 3 個連續單引號 ''' 或雙引號 """, 解譯器遇到 ''' 或 """ 時會忽略之後一直到下一個 ''' 或 """ 間之敘述. 在函數定義的最前面都會用多行註解來製作函數的使用說明. 
多行註解方式也可用來製作多行字串, 例如 :

>>> msg='''hello 
world! 
Tony'''  
>>> print(msg)
'hello\nworld!\nTony'

函式內容的第一行應該用多行註解撰寫該函式之功能或摘要, 若後面還有詳細說明, 須先空一行, 再寫說明註解. 例如 :

def get_circle_area() :
    """此函式用來計算圓面積"""

    #圓面積公式=pi*r^2

Python 的函數, 類別, 模組都有一個內建屬性可用來取得其第一行註解, 例如 : 

>>> def get_circle_area() :     
    """此函式用來計算圓面積"""    
    
>>> print(get_circle_area.__doc__)
此函式用來計算圓面積


四. 關鍵字 (keywords) 與識別字 (identifiers)  :

識別字是程式語言中用來命名變數, 常數, 函數, 或物件的詞彙, 它是指向這些物件在記憶體中的參考位址. 關鍵字則是 Python 保留不可用做識別字的詞彙, Python 3 有 33 個關鍵字, 這些關鍵字不能被用作變數或函數等之識別字, 否則執行時會出現語法錯誤 :


Falseclassfinallyisreturn
Nonecontinueforlambdatry
Truedeffromnonlocalwhile
anddelglobalnotwith
aselififoryield
assertelseimportpass
breakexceptinraise


原本在 Python 2 有 31 個關鍵字, 但其中的 print 與 exec 這兩個關鍵字到 Python 3 後被函數化, 於是變成 29 個; Pyhton 3 又另外增加了 None, False, True, 以及 nonlocal 這四個關鍵字, 到 Python 3.4 開始因支援非同步又增加 async 與 await, 總共是 35 個, 參考 :


Python 的關鍵字可用過內建模組 keyword 的 keylist 屬性查得, 例如 :

>>> import keyword
>>> keyword.kwlist 
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
>>> len(keyword.kwlist) 
35

Python 的識別字有如下限制 :
  1. 有分大小寫. 
  2. 只能使用英文字母, 底線或數字 (但第一個字元不可以是數字). 
  3. 不可使用 Python 關鍵字.
  4. 不要使用與內建函數同名之變數, 例如 str, len, int 等. 
簡言之, 識別字不可用數字開頭, 字元中間不可有空白, 也不能使用特殊字元例如 $, #, %, ? 等等. 雖然 Python 3 以後全面支援 Unicode, 可使用中文字當變數, 但因輸入與維護困難以及可攜性低, 故不建議使用.

>>> 訊息="哈囉"    #可以用中文當變數 (但可攜性低)
>>> print(訊息) 
哈囉

使用關鍵字當識別字會出現語法錯誤, 例如 as (但 As 或 AS, aS 則可) :

>>> as=1                    #不可使用保留字當變數名稱
  File "<stdin>", line 1
    as=1
     ^
SyntaxError: invalid syntax       
>>> As=1                    #As 不是保留字可以用
>>> As
1

下面是不合法的識別字例子 :

2name               (以數字開頭)
_getter!@         (含有特殊字元 ! 與 @)
ab c-de               (不可使用 - 或空格)

Python 是物件導向語言, 其類別與方法名稱之命名方式亦須遵守上述變數與自訂函數識別字之規範. 此外, 類別還有非強制的命名慣例如下 :
  1. 類別名稱: 慣用首字大寫駝峰字, 例如 ClassName 或 Class_Name
  2. 方法名稱: 慣用首字小寫駝峰字, 例如 methodName 或 method_Name


五. 輸出與輸入 : 

從標準輸入 (鍵盤) 讀取使用者輸入可呼叫內建函數 input(str), 傳入參數為提示字串, 其傳回值類型為字串, 故若輸入值為後續要進行計算的數值, 則進行計算前必須呼叫 int(), float() 等內建函數強制轉型, 例如 :

>>> name=input("Please enter your name=")
Please enter your name=tony
>>> type(name)
<class 'str'>             # input() 傳回值為字串類型
>>> price=input("請輸入定價=")
請輸入定價=100
>>> price*0.8
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'float'
>>> price=float(input("請輸入定價="))      # 呼叫 float() 將 input() 的傳回值轉成數值
請輸入定價=100
>>> price*0.8
80.0  

若要一次輸入多個變數, 可用 Python 的同步指定與字串物件的 split() 方法處理, 例如 :

>>> account, pwd=input("請輸入帳號密碼, 以空格隔開:").split() 
請輸入帳號密碼, 以空格隔開:tony 123456
>>> account
'tony'
>>> pwd
'123456' 

輸出到 Console 則可呼叫內建函數 print(msg), 傳入參數為字串 : 

>>> print("account=" + account + " pwd=" + pwd)
account=tony pwd=123456


六. 資料型態 :

Python 的內建資料型態可分成 3 類共 9 個型別 :


 資料型態種類 說明
 Numeric (數值) int, float bool, complex
 String (字串) str
 Container (容器) tuple, list, dict, set


這 9 種資料型別的關係結構如下圖所示 :




其中 protocol 類別為四個數值型別 (int, float, bool, complex) 的父類別, 它們都是不可迭代的 (non-iterable), 也是不可改變的 (immutable). 序列型別有三種 : str, tuple, 與 list, 而可迭代 (iterable) 型別共有五種, 即三種序列型別再加上 dict 與 set, 其中僅 str 與 tuple 為不可改變, 其餘 list, dict, set 均可改變. 

不可變型別的物件如數值, 字串, 與元組一旦建立, 其內容即固定不變 (即均為常數). 一個指向不可變型別物件的變數, 其賦值改變時並非該物件內容被改變, 而是建立了一個新的物件實體, 然後將變數指向了新的實體, 例如 :

name="tony"
name="peter"

並非字串物件 "tony" 被改成 "peter", 而是建立了一個新的字串物件 "peter", 並將 name 變數指向新實體, 而原來的 "tony" 若無其他變數指向它, 將被系統回收.

容器型別物件 str, tuple, list, dict,與 set 又可按其元素分為有無順序與可否重複, 這五種容器只有 dict 與 set 無順序, 只有 set 元素不可重複, 摘要如下表 :


 特性 int float bool complex str tuple list dict set
 可迭代 no no no no yes yes yes yes yes
 可改變 no no no no no yes yes yes yes
 有順序 x x x x yes yes yes no no
 可重複 x x x x yes yes yes yes no



1. 數值型別 : 

Python 的數值型別有 4 種 :

 數值型別 說明
 int 整數, Python 3 以後範圍只受到記憶體限制.
 float 浮點數, 可用小數點或科學表示法
 bool 布林值, 只有 True (=1) 與 False (=0) 兩個值.
 complex 複數, 由實部 (real) 與虛部 (imag) 組成, 例如 2+i. 


(1). 整數 (int) :

整數在 Python 2 時有分 int (整數, 固定精準度) 與 long (長整數, 無限精準度), 但 Python 3 之後只有無限精準度的長整數, 其型別名稱為 int (不是 long) : 

>>> type(1)                  #整數的型別名稱為 int
<class 'int'> 

Python 3 可表示的整數大小不受限制, 例如 Google 命名由來的 Googol 是 10 的 100 次方 : 

>>> googol=10**100 
>>> googol       
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

注意, 內建系統模組 sys 中有一個 maxsize 屬性, 但這與整數最大值無關, 而是代表 Python 容器類資料 (例如字串, 串列, 與元組等) 的索引之最大值, 亦即容器物件的長度不可超過此值 : 

>>> import sys                   # 引入 sys 模組
>>> sys.maxsize                 # 顯示 maxsize 索引之最大值
9223372036854775807       # for 64-bit 系統
>>> -sys.maxsize-1   
-9223372036854775808 

參考 :

sys.maxsize() in Python

整數可用十進位 (decimal), 二進位 (binary), 八進位 (octal), 以及十六進位 (hexadecimal) 表示, 除了十進位外, 其他進位系統都必須用前置碼 (prefix) 表示其基底 (base), 整理如下表 :


 整數基底 (base) 前置碼 (prefix) 說明
 2 進位 (0~1) 0b 或 0B 0b1100 等於 10 進位之 12
 8 進位 (0~7) 0o 或 0O 0o14 等於 10 進位之 12
 10 進位 (0~9) 無 12 (不可用 012)
 16 進位 (0~9, A~F) 0x 或 0X 0xc 等於 10 進位之 12


注意, 前置碼大小寫皆可, 其次, 十進位沒有前置碼, 前面不可補 0, 例如以 print() 函數 :

>>> 012                          #十進位數不可以補 0
  File "<stdin>", line 1
    012
      ^
SyntaxError: invalid token
>>> 0b1100                     #二進位的 12
12
>>> 0o14                         #八進位的 12     
12
>>> 0xc                            #十六進位的 12
12
>>> a=0b1100 + 0o14     #二進位加八進位結果會以十進位表示
>>> print(a)
24

可見用 print() 函數列印用非十進位表示的數值時,  Python 會將其轉成十進位. Python 提供了下列內建函數可用來進行整數的基底或型態轉換 :


 整數轉換函數 說明
 bin(d) 將十進位整數 d 轉成二進位後以字串型態傳回
 oct(d) 將十進位整數 d 轉成八進位後以字串型態傳回
 hex(d) 將十進位整數 d 轉成十六進位後以字串型態傳回
 int(o) 將物件 o 轉成 int 型態十進位整數 
 int(o, base) 將物件 o 轉成基底為 base 的 int 型態整數


注意, bin(), oct(), hec() 這三個函數只能傳入十進位整數值 (必要參數, 不傳入會出現錯誤); 而 int() 只能傳入整數字串或布林值, 例如 :

>>> bin(12)           #10 進位轉 2 進位
'0b1100'
>>> oct(12)            #10 進位轉 8 進位
'0o14'
>>> hex(12)           #10 進位轉 16 進位
'0xc'
>>> hex(255)         #10 進位轉 16 進位
'0xff'
>>> int('12')          #字串轉整數
12
>>> int('012')        #字串轉整數, 前面的 0 會被忽略
12
>>> int(True)         #不林值轉整數
1


(2). 浮點數 (float) :

浮點數即帶有小數點之數值, 例如圓周率 3.14159, 精密度為 double (倍精度), 大約是到小數點後 17 位, 例如在 Win10 與 Raspberry Pi 3 上分別計算如下無限小數 :

Python 3.5.3 (default, Sep 27 2018, 17:25:39)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 22/7 
3.142857142857143
>>> import math 
>>> math.sqrt(2) 
1.4142135623730951
>>> math.e 
2.718281828459045 

Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> 22/7   
3.142857142857143
>>> import math 
>>> math.sqrt(2) 
1.4142135623730951
>>> math.e 
2.718281828459045

浮點數也可以用科學表示法表示, 例如普郎克常數 6.62607*10**(-34) 的科學表示法為 6.62607e-34 或 6.62607E-34 (焦耳秒), 其中 e 或 E (大小寫均可) 表示底數 10, 後面的 -34 為次方, 小於 1 的浮點數若超過小數點四位數就會自動以科學表示法來表示, 例如 :

>>> type(1.234)               #浮點數型別名稱為 float
<class 'float'>
>>> type(1.234e-12) 
<class 'float'>
>>> 6.62607e-34              #科學表示法用 e/E 均可
6.62607e-34
>>> 6.62607E-34
6.62607e-34
>>> 12.3456789e-89        #自動調整為標準科學表示法
1.23456789e-88 
>>> 0.0001
0.0001
>>> 0.00001                      #超過小數點四位數用科學表示法
1e-05

Python 內建函數 float() 可將布林值, 整數, 數值 (整數或浮點數) 字串轉換成浮點數, 但不能轉換複數, 例如 :

>>> float(2)                    #整數轉浮點數
2.0
>>> float(True)              #布林值轉浮點數
1.0
>>> float(False) 
0.0
>>> float("2")                #整數字串轉浮點數
2.0
>>> float("3.14159")     #浮點數字串轉浮點數
3.14159
>>> float(1+2j)               #float() 不可轉換複數
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't convert complex to float


(3). 布林數 (bool) :

布林數只有 True 與 False 兩個值, 用在邏輯運算, 但它其實是整數的子類型, 因為 True 的值為 1, False 的值為 0. 將布林數與整數做運算時, True 會用 1 代替, False 會用 0 代替進行運算, 用 int() 轉換 True 與 False 會分別傳回 1 與 0 :

>>> type(True)     #布林值的型別名稱為 bool
<class 'bool'>
>>> int(True)        #轉成整數
1
>>> int(False)       #轉成整數
0
>>> 5 + True         #True 其實就是整數 1
6
>>> 4 * False         #False 其實就是整數 0
0
>>> 1 + False 
1
>>> 100/False         #發生除以 0 錯誤
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

內建函數 bool() 可將傳入之其他型態資料轉成布林值, 除了 0, 0.0, False, None, 空字串 '', 空串列 [], 空元組 (), 空字典 {}, 與空集合 set() 等會被轉成 False 外, 其餘都會被轉成 True. 例如 :

>>> bool(1)   
True
>>> bool('ok')  
True
>>> bool('')   
False
>>> bool(' ')   
True
>>> bool(None)   
False
>>> bool(0)   
False
>>> bool(0.0)   
False
>>> bool(())   
False
>>> bool([])   
False
>>> bool({})   
False
>>> bool(None)   
False


(4). 複數 (complex) :

複數由實部 (real) 與虛部 (imaginary) 組成, 數學中通常使用 i 表示虛部, 工程界則偏好使用 j 表示虛數, 但在 Python 中虛部必須用 j 或 J 標示 (大小寫均可), 不可用 i 或 I.

其次, 虛部數值可在 j 前面或後面, 即一個複數可表示為 z=a +  b j 或 z=a +  j b. 其中 a 與 b 可為整數或浮點數. 但注意, 虛部值為 1 時必須寫出不可省略, 否則會出現 "j is not defined" 錯誤, 例如 :

>>> type(1+1j)         #複數型別名稱為 "complex"
<class 'complex'>
>>> x=1+1j               #虛部值為 1 時必須寫出來
>>> y=1-1j                #虛部值為 1 時必須寫出來
>>> x*y                     #複數乘法
(2+0j)
>>> x+y                     #複數加法
(2+0j)
>>> x/y                      #複數除法
1j
>>> (1+2j)/(1-2j)       #複數除法
(-0.6+0.8j)
>>> x=1+j                 #虛部值為 1 時必須寫出來
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'j' is not defined

Python 有一個內建函數 complex(real, imag) 可傳入兩個參數 (第一參數為實部, 第二為虛部) 來建立一個複數, 例如 :

>>> z=complex(1,2)         #呼叫 complex() 建立複數
>>> type(z)
<class 'complex'>     
>>> print(z)
(1+2j)

此外 complex() 亦可將複數字串, 整數, 浮點數或布林值等轉成複數, 例如 :

>>> x=complex(1)   
>>> x 
(1+0j)
>>> type(x)
<class 'complex'> 
>>> x=complex('1+1j')       #將複數字串轉成複數
>>> x 
(1+1j) 
>>> type(x)   
<class 'complex'>   
>>> complex(False)            #布林值 False 會被轉成 0j
0j
>>> complex(True)             #布林值 True 會被轉成 1+0j
(1+0j)

複數本身也是一個物件, 有 real 與 imag 兩個屬性用來分別儲存實部與虛部之值, 還有一個會傳回其共軛複數的 conjugate() 方法, 例如 :

>>> z=1+2j                  #複數字面值
>>> z
(1+2j)
>>> z.real                     #實部
1.0
>>> z.imag                   #虛部
2.0
>>> z.conjugate()        #傳回共軛複數
(1-2j)


2. 字串 (str) : 

字串可以用引號直接建立字面值 (literal) 或呼叫內建函數 str() 建立字串物件. 用引號建立字串物件有單行與多行兩種表示法 : 

(1). 單行字串 :

Python 的字串可用單引號 '' 或雙引號 "" 括起來, 如果單引號裡面內含單引號, 或雙引號裡面內含雙引號, 則裡面的引號需用倒斜線 \ 跳脫, 或者採取內單外雙/內雙外單交替的方式, 例如 :

>>> print("I'm Tony")   
I'm Tony
>>> print('I\'m Tony')  
I'm Tony

長字串可用 "+" 與 "\" 分行串接, 例如 :

>>> s="Hello " + \
...   "World"
>>> print(s)
Hello World

注意, "\" 後面必須直接跳行, 不可有空格.


(2). 多行字串 : 

Python 也直接提供多行字串語法, 連續三個單引號或多引號之間的字串為多行字串, 每一行之間會以跳行字元 '\n' 字元隔開, 例如 :

>>> s='''Tony,
... Kelly,
... Peter,
... Amy'''
>>> s
'Tony,\nKelly,\nPeter,\nAmy'
>>> print(s)
Tony,
Kelly,
Peter,
Amy

可見直接輸入變數名稱會顯示多行字串的原始內容 (包含跳行字元 \n), 但若呼叫 print() 則跳行符號會被解讀. 

也可以呼叫內建函數 str() 來建立字串, 特別是要將數值物件轉成字串物件時, 例如 :

>>> s="123"      
>>> type(s)   
<class 'str'>            #字串物件 str
>>> s=str(123)   
>>> type(s)   
<class 'str'>            #字串物件 str

注意, 因為內建函數 str() 的存在, 故不要將字串變數命名為 str, 否則 Python 解譯器會將 str 認為是變數使得呼叫 str() 時發生 "not callable" 錯誤, 例如 :

>>> str="123"              # str 參考指向變數 str
>>> s1=str(123)           # 無法呼叫 ()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable     
>>> del str                  # 刪除會造成混淆的 str 變數
>>> s1=str(123)          # str 恢復指向內建函數 str() 而可呼叫
>>> s1    
'123'

字串是不可變 (immutable) 型別, 字串變數一經賦值即不可更改, 對同一變數重複賦值並非更改其內容, 而是建立一個新字串, 該變數改成指向新字串, 這可用內建函數 id() 檢驗, 此函數會傳回物件之參考索引 (指向該字串在記憶體中的位址), 例如 :

>>> s="abc"   
>>> id(s)    
2648457405808   
>>> s="123"        
>>> id(s)     
2648460584304         # s 指向新字串物件

可見字串變數 s 的內容並非被改成 "abc", 而是指向新字串 "123". 另外, Python 沒有字元型別, 字元是以字串來表示, 例如 :

>>> c='a' 
>>> print(c)   
'a'
>>> c="A"   
>>> print(c)   
'A'


(3). 字串的長度 (字元數) :

計算字串的長度 (字元數) 可用 Python 內建函數 len(), 注意, 跳脫字元算一個字元, 不是兩個字元, 例如 :

>>> len('Hello\nWorld')
11

此例 Hello 與 World 中間有一個跳行字元 "\n", 所以加起來是 11 個字元. 中文一個字元的長度也是 1, 例如 :  

>>> len("我")   
1
>>> len("您好")    
2
>>> len("我\n愛\n你")   
5


(4). 字串的運算 : 

字串的運算包括 : 串接, 複製, 與比較. 多個字串字面值可用 "+" 運算子直接串接, 例如 :

>>> 'Hello' + ' ' + 'World' 
'Hello World'
>>> 'Hello' + '\n' + 'World'     
'Hello\nWorld'
>>> print('Hello' + '\n' + 'World')     
Hello
World

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

>>> a='abc123'
>>> a '456'              #字串變數不可直接串接
>>> a '456' 
  File "<pyshell>", line 1
    a '456'
          ^
SyntaxError: invalid syntax
>>> a + '456'  
'abc123456'

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

>>> '123' + 123            #Python 不會自動轉型
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
>>> int('123') + 123      #必須強制轉型
246

一個字串可用乘法運算子 "*" 與一個正整數相乘而複製, 若乘以 0 或負整數將得到空字串 :

>>> "Hello" * 3          #乘以正整數複製
'HelloHelloHello'
>>> "Hello" * 0          #乘以 0 得空字串
''
>>> "Hello" * (-3)      #乘以負整數得空字串
''

字串可以用比較運算子依序根據 Unicode 值之大小進行比較 :

>>> "a" > "b" 
False
>>> "a" < "b" 
True
>>> "a" == "a" 
True
>>> "a" != "b" 
True
>>> "abc" > "abb"     
True


(5). 字串切片 (slice) :

所謂切片 (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 次
例如 :

>>> a='0123456789'    
>>> a[:]         #擷取整個字串
'0123456789'
>>> a[5:]       #擷取索引 5 到字串尾
'56789'
>>> a[:5]       #擷取字串首至索引 4
'01234'
>>> a[3:8]     #擷取索引 3 到索引 7
'34567'
>>> a[3:8:2]   
'357' 
>>> a[3:8:1]   #與 [3:8] 效果相同 (step 預設為 1)
'34567'

注意, 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 也可以是負數, 表示由後往前 (或由右往左) 跳躍, 例如 :

>>> a='0123456789'    
>>> a[::-1]          #間隔 -1 表示從右到左每次取一個
'9876543210'

這個 [::-1] 是指整個字串 [::] 從後面每次取一個 


(6). 字串方法 :

字串物件提供許多方法, 摘要如下表 : 


 字串物件之方法 說明
 capitalize() 將首字母大寫, 其餘小寫
 lower() 將全部字元轉成小寫
 upper() 將全部字元轉成大寫
 swapcase() 將字元大寫變小寫, 小寫變大寫  
 title() 將每個單字的首字元大寫, 其餘小寫  
 islower() 字串中的英文字母都是小寫傳回真, 否則傳回假
 isupper() 字串中的英文字母都是大寫傳回真, 否則傳回假
 istitle() 字串中每個單字的首字元大寫其餘小寫傳回真, 否則傳回假
 isspace() 字串只含有空白字元 ('\t','\v','\n','\r','\f',' ') 傳回真, 否則傳回假
 isalpha() 字串只含有英文字母傳回真, 否則傳回假
 isdigit() 字串只含有數字 (0~9) 傳回真, 否則傳回假
 isdecimal() 字串只含有 10 進位數字傳回真, 否則傳回假 
 isalnum() 字串只含有英文字母與數字傳回真, 否則傳回假 
 isidentifier() 字串是否為合法的識別字
 isprintable() 字串只含有可見字元 
 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 結尾傳回真, 否則傳回假
 replace(old, new [, count]]) 將子字串 old 全部用 new 替換, 或指定替換 count 個
 strip([chars]) 去除字串首尾的指定字元集合 chars, 預設刪除空白 
 lstrip([chars]) 去除字串開頭的指定字元集合 chars, 預設刪除空白 
 rstrip([chars]) 去除字串結尾的指定字元集合 chars, 預設刪除空白
 center(width [,fillchar]) 將字串居中擴展為 width 個字元, 左右以 fillchar 填充, 預設為空格
 ljust(width [,fillchar]) 將字串靠左擴展為 width 個字元, 右方以 fillchar 填充, 預設為空格
 rjust(width [,fillchar]) 將字串靠右擴展為 width 個字元, 左方以 fillchar 填充, 預設為空格
 zfill(width) 在字串左方補 0 使總長度擴展為 width 字元
 expandtabs([tabsize]) 將字串中的 tab 字元以 tabsize 個空格取代, 預設 8 格 
 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)
 join(iterable) 以字串為分隔字元串接 iterable 物件各元素為新字串後傳回


參考 :

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

常用範例如下 :

join() 方法用來串列序列資料的元素 :

>>> lst=['Heelo', 'World', '!']    
>>> ' '.join(lst)     
'Heelo World !'  
>>> '-'.join(lst)    
'Heelo-World-!'  

split() 方法用來將字串以指定分界字串 (預設為空格) 拆成串列 : 

>>> str='Hello World !'   
>>> str.split(' ')   
['Hello', 'World', '!']
>>> str.split()              # 預設以空格拆分
['Hello', 'World', '!']

startswith() 與 endswith() 用來檢查字串是否以特定字串開頭或結束, 例如 :

>>> str='https://tony1966.github.com'    
>>> str.startswith('http')    
True
>>> str.startswith('http:')    
False
>>> str.startswith('https:')    
True
>>> str.endswith('com')    
True
>>> str.endswith('tw')       
False

ljust() rjust(), 與 center() 為字串排版方法 (這在列印報表時很常用), 它們的第一參數指定字串的寬度占多少字元, 備選的第二參數則指定剩餘部分要用甚麼字元填滿 (預設是空格), 例如 :

>>> str='Hello'   
>>> str.ljust(10)   
'Hello     '
>>> str.ljust(10, '*')   
'Hello*****'
>>> str.rjust(10)   
'     Hello'
>>> str.rjust(10, '*')     
'*****Hello'
>>> str.center(10)   
'  Hello   '
>>> str.center(10, '*')   
'**Hello***'

zfill() 方法則是在前面剩餘處都補 0, 主要用在數值字串輸出, 例如 :

>>> price='123.456'   
>>> price.zfill(10)    
'000123.456'

lstrip(), rstrip(), 與 strip() 方法用來清除字串頭尾的空格, lstrip() 只清除前面的空格; rstrip() 只清除後面的空格; 而 strip() 則是頭尾的空格都清除, 傳回清除後的字串 (原字串不受影響, 因字串是不可更改的資料型態), 例如 : 

>>> str='   Hello   '
>>> str.lstrip()   
'Hello   '
>>> str.rstrip()        
'   Hello'
>>> str.strip()   
'Hello'

find() 方法用來搜尋指定子字串之位置, 傳回該子字串開頭字元的索引, 未找到傳回 -1, 例如 :

>>> str='Hello World !'    
>>> str.find('W')     
6
>>> str.find('e')     
1
>>> str.find('llo')     
2
>>> str.find('ko')   
-1

replace(old, new [, count]) 用來將原子字串 old 以新字串取代, 也可指定取代次數, 例如 : 

>>> str='Hello World !'    
>>> str.replace(' ', '-')     
'Hello-World-!'
>>> str.replace(' ', '-', 1)      
'Hello-World !'
>>> date='2022-08-25'     
>>> date.replace('-', '/')    
'2022/08/25'


3. 串列 (list) : 

串列是可變有序的群集 (mutable and ordered collection), 實際上是一種可被索引的動態陣列 (dynamic array) 結構, 其元素可以是任何物件, 亦即允許不同型別之異質元素 (heterogeneous), 包括串列甚至函數都可以, 且可以隨時新增或移除元素. 為了可存放異質元素, 其資料以較稀疏的形式存放, 故所佔之記憶空間也比 C 語言中的相同型別 (同質) 陣列要大.


(1). 建立串列物件 :

串列元素用中括號運算子 [] 包圍, 元素之間用逗號隔開, 串列字面值 (literal) 語法格式如下 :

list1=[e1, e2, e3, ....]

與 tuple 不同的是, 如果只有一個元素, 元素後面不需要加逗點 (加不加逗號無所謂, 元組則一定要加, 否則型態不是 tuple). 查詢串列的元素個數可用內建函數 len(), 例如 :

>>> list1=[123, 12.3, True, False, 7+8j, "abc",  (1, 2, 3), ['A', 'B', 'C']]   
>>> type(list1)   
<class 'list'>
>>> list1 
[123, 12.3, True, False, (7+8j), 'abc', (1, 2, 3), ['A', 'B', 'C']]
>>> len(list1)      #查詢串列長度
8
>>> list2=[123]      # 不需加逗號, 但加也可以 [123,]
>>> type(list2)   
<class 'list'>            # 型態為串列

除了使用字面值來建立串列外, 還可以呼叫內建函數 list() 來建立串列物件, 然後再呼叫串列物件的 append(e) 或 insert(i, e) 方法來添加元素, 前者會將元素 e 添加到串列尾端; 而後者則是將元素 e 插入到指定索引 i, 例如 :

>>> list1=list()                #建立空陣列
>>> type(list1) 
<class 'list'>   
>>> list1.append('a')      #添加元素至尾端
>>> list1
['a']
>>> list1.append('i')       #添加元素至尾端
>>> list1 
['a', 'i']
>>> list1.insert(1, 'u')     #插入元素於指定索引
>>> list1   
['a', 'u', 'i']

串列物件的方法如下表 :


 串列物件的方法 說明
 index(元素) 傳回指定元素之索引
 count(元素) 傳回指定元素出現的次數
 append(元素) 將元素添加於串列尾端
 extend(可迭代物件) 將可迭代物件 (字串, 串列, 元組, 集合等) 添加於串列尾端
 insert(索引, 元素) 將元素插入串列中之指定索引處
 remove(元素) 從串列中刪除指定元素
 del list_var[n1:n2:n3] 刪除串列 list_var 中索引 n1~(n2-1) 之元素, 若傳入 n3 則每隔 n3 個
 sort() 將串列元素由小到大排序 (會改變串列本身)
 reverse() 將串列元素順序反轉


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

>>> fruits=['apple', 'banana', 'melon']   
>>> fruits.index('apple')     
0
>>> fruits.index('banana')    
1
>>> fruits.index('melon')   
2

呼叫 append() 可以新增元素到串列的尾端 (串列元素可重複), 例如 :

>>> fruits.append('banana')             # 新增元素至串列尾端
>>> fruits   
['apple', 'banana', 'melon', 'banana']   
>>> fruits.append('grape')                    # 新增元素至串列尾端
>>> fruits   
['apple', 'banana', 'melon', 'banana', 'grape']  

方法 append() 也可以傳入其他可迭代物件 (元組, 串列, 集合等), 但它們會以單一元素身分加入, 而不會打散; 即使是添加一個空串列也會加入成為元素, 例如 : 

>>> fruits=['apple', 'banana', 'melon'] 
>>> fruits.append(['banana', 'grape'])   
>>> fruits    
['apple', 'banana', 'melon', ['banana', 'grape']]    
>>> fruits.append([])     # 添加空串列當元素
>>> fruits   
['apple', 'banana', 'melon', ['banana', 'grape'], []]   
>>> fruits.append('')      # 添加空字串當元素
>>> fruits.append({})     # 添加空集合當元素
>>> fruits   
['apple', 'banana', 'melon', ['banana', 'grape'], [], '', {}]   

呼叫 extend() 方法可以添加一個可迭代物件 (字串, 元組, 串列, 集合等) 到串列尾端, 但這個可迭代物件的全部元素會被打散, 以 '個人' 身分加入串列中, 例如 : 

>>> fruits=['apple', 'banana', 'melon']   
>>> fruits.extend('grape')   
>>> fruits   
['apple', 'banana', 'melon', 'g', 'r', 'a', 'p', 'e']    

可見字串 'grapes' 被一個一個字元拆開後依序添加到串列尾端 (因為字串是可迭代物件). 如果不想讓字串被打散成字元, 則要將其放入串列, 元組, 或集合中, 例如 : 

>>> fruits.extend(['grape'])   
>>> fruits   
['apple', 'banana', 'melon', 'g', 'r', 'a', 'p', 'e', 'grape']   
>>> fruits.extend({'banana', 'guava'})      # 將集合元素加入串列
>>> fruits   
['apple', 'banana', 'melon', 'g', 'r', 'a', 'p', 'e', 'grape', 'banana', 'guava']

但如果用 extend 加入一個空的可迭代物件, 就不會像 append() 那樣將空串列, 空元組, 或空集合加入串列中, 例如 : 

>>> fruits=['apple', 'banana', 'melon']    
>>> fruits.extend('')          # 不會添加空字串於串列中
>>> fruits   
['apple', 'banana', 'melon']
>>> fruits.extend([])          # 不會添加空串列於串列中
>>> fruits   
['apple', 'banana', 'melon']
>>> fruits.extend({})         # 不會添加空集合於串列中
>>> fruits   
['apple', 'banana', 'melon']  

但如果是將空字串, 空串列, 空集合等組成的可迭代物件傳入 extend() 中, 則會打散後添加於串列中, 例如 :

>>> fruits.extend(['', {}])   
>>> fruits  
['apple', 'banana', 'melon', '', {}]

呼叫 count() 方法會傳回傳入之元素在串列中的數目, 例如 :

>>> fruits   
['apple', 'banana', 'melon', 'banana', 'grape']
>>> fruits.count('apple')                 # 串列中有 1 個 'banana'
1
>>> fruits.count('banana')             # 串列中有 2 個 'banana'
2
>>> fruits.count('pineapple')         # 串列中無 'pineapple'
0

呼叫 insert() 可在指定索引位置 (第一參數) 插入元素 (第二參數), 例如 : 

>>> fruits   
['apple', 'banana', 'melon', 'banana', 'grape']
>>> fruits.insert(2, 'orange')       # 在索引 2 位置插入元素 'orange'
>>> fruits    
['apple', 'banana', 'orange', 'melon', 'banana', 'grape']

可見新元素 'orange' 被插入於索引 2, 而原先索引 2 的 'melon' 與之後的元素都往後挪一格. 

呼叫 remove() 方法可以刪除指定之元素, 如果該元素在串列中有多個, 則會刪除第一個 (最前面的), 不會全部刪掉, 例如 : 

>>> fruits   
['apple', 'banana', 'orange', 'melon', 'banana', 'grape']      # 有兩個 'banana'   
>>> fruits.remove('banana')        # 刪除第一個 'banana'
>>> fruits      
['apple', 'orange', 'melon', 'banana', 'grape']      # 第 2 個 'banana' 還在

如果要一次刪除連續或固定間隔的多個元素則可以用 del list_var[n1:n2:n3] 指令, 其中 n1 是起始索引, n2 是終止索引 (不包含 n2), n3 是間隔, 其中只有 n1 是必須參數, n2 與 n3 可有可無, 若只傳入 n1 表示只刪除 n1 元素; n3 預設是 1, 表示連續不跳格, 例如 :

>>> fruits   
['apple', 'orange', 'melon', 'banana', 'grape']
>>> del fruits[1:3]              # 刪除索引 1~2 元素 (即 'orange' 與 'melon')
>>> fruits  
['apple', 'banana', 'grape']     

可見索引 1 至 2 元素的元素已被刪除. 也可以指定 n3 參數作為步階, 例如 : 

>>> fruits=['apple', 'banana', 'orange', 'melon', 'banana', 'grape']   
>>> fruits   
['apple', 'banana', 'orange', 'melon', 'banana', 'grape']    
>>> del fruits[1:-1:2]      # 以步階 2 刪除索引 1 至最後元素 (不包含)
>>> fruits    
['apple', 'orange', 'banana', 'grape']

此立 n2=-1 表示最後元素但不包含, 所以會刪除索引 1 與 3 元素. 

呼叫 sort() 方法會將串列元素以升冪 (小到大) 方式排列, 會改變串列本身, 例如 :

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

如果是元素含有字串, 則逐字元以 ASCII 碼順序做升冪排列, 例如 : 

>>> a=['c', 'a', 'b', 'aa', 'ba', 'cb']   
>>> a.sort()    
>>> a    
['a', 'aa', 'b', 'ba', 'c', 'cb']

可見會從第一個字元開始排序, 所以 'aa' 會排在 'b' 前面. 但注意, sort() 只能對同質串列排序, 亦即元素不可混雜數值與字串, 否則會出現 TypeError 錯誤, 例如 : 

>>> a=['a', 'b', 'c', 3, 1, 2]  
>>> a.sort()   
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'str'

如果要做降冪排序 (由大到小), 則可先呼叫 sort() 後再呼叫 reverse() 方法, 例如 : 

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

注意, 不可以連續呼叫 sort() 與 reverse(), 因為 sort() 沒有傳回值 (其實是傳回 None) :

>>> a=[4, 1, 2, 5, 3]   
>>> a.sort().reverse()       
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'reverse'

由上可知, sort() 方法是直接改變串列本身元素的順序, 如果想保持串列元素原本的順序, 可以使用 Python 內建函式 sorted(串列 [, reverse=False]), 它會傳回排序後的新串列, 例如 : 

>>> a=[4, 1, 2, 5, 3]   
>>> b=sorted(a)       # 參數 reverse 預設 False (升冪排序)
>>> b   
[1, 2, 3, 4, 5]   
>>> a                         # 原串列 a 不變
[4, 1, 2, 5, 3]
>>> c=sorted(a, reverse=True)      
>>> c     
[5, 4, 3, 2, 1]
>>> a                         # 原串列 a 不變
[4, 1, 2, 5, 3]

參考 :


除了 sorted() 函式外, 串列常用的 Python 內建函式有下列三個 :
  • len() : 傳回串列元素個數 (長度)
  • min() : 傳回串列元素中值最小者
  • max() : 傳回串列元素中值最大者
例如 : 

>>> a=[4, 1, 2, 5, 3]   
>>> len(a)   
5
>>> min(a)   
1
>>> max(a)   
5
>>> a=['C', 'B', 'A']   
>>> min(a)   
'A' 
>>> max(a)   
'C'

如果元素是字串, 則以其 ASCII 碼大小逐字元比較. 


4. 元組 (tuple) : 

元組是不可變, 有序的群集 (unmutable and ordered collection), 它與串列一樣都是有序的序列結構, 差別僅僅是元組的元素不可變而已 (即 tuple 建立後不能再新增元素或更改任一元素之值). 

建立 tuple 字面值的方法是用指定運算子將元素以逗號隔開指派給變數即可, 也可以用小括弧將資料括起來, 呼叫內建函數 len() 會傳回元素數目, 例如 : 

>>> a=1, 2, 3       # 外面沒有小括弧也可以
>>> type(a)    
<class 'tuple'>
>>> a    
(1, 2, 3)
>>> a=(1, 2, 3)     # 外面有小括弧
>>> type(a)    
<class 'tuple'>
>>> a    
(1, 2, 3)
>>> len(a)       # 查詢元素數目
3

可見建立時不論有無小括號, 顯示元組內容時都會有小括號. 但要注意, 如果元組只有一個元素, 其後需加一個逗號, 否則型態會是基本型態物件, 而不是 tuple, 例如 : 

>>> a=(123)         # 元素後面無逗號 : 不是元組
>>> type(a)   
<class 'int'>   
>>> a=(123,)        # 元素後面有逗號 : 是元組
>>> type(a)    
<class 'tuple'>       
>>> a=123            # 元素後面無逗號 : 不是元組
>>> type(a)   
<class 'int'>    
>>> a=123,          # 元素後面有逗號 : 是元組
>>> type(a)   
<class 'tuple'>   

取得元組內元素的方法與串列一樣, 使用中括號運算子 [索引] 取得, 也可以用迴圈走訪, 因為元組是不可變資料, 更改元素之值會出現錯誤

>>> a=1, 2, 3     
>>> print(a[0], a[1], a[2])      # 用 [索引] 取得
1 2 3
>>> for i in a:      # 用迴圈走訪元素
    print(i)  
    
1
2
3
>>> a[0]=100        # 元組的內容是不可變的
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment  

元組的元素可以是異質的 (即不同資料型態), 也可以是另一個元組, 例如 :

>>> t=('debby', 28, True, (45, 26, 44))    # 具有異質元素的 tuple
>>> t   
('debby', 28, True, (45, 26, 44))
>>> for i in t:      
    print(i)    
    
debby 
28 
True 
(45, 26, 44) 

可以用指定運算子將元組解包 (unpack) 給多個變數, 但要注意變數個數必須等於 tuple 元素個數, 否則會發生解包錯誤 : 

>>> a=(1, 2, 3) 
>>> x, y, z=a         # 用指定運算子解包 tuple 內之元素到多個變數
>>> print(x, y, z)    
1 2 3
>>> x, y=a              # 解包變數數目與 tuple 元素數目不符導致錯誤
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
ValueError: too many values to unpack (expected 2)

如果要在解包時避免數目不符錯誤, 可在某變數前面加 "*" 符號, 表示接收其餘元素, 但此變數會變成 list 型態, 例如 :

>>> a=(1, 2, 3) 
>>> *x, y=a          # 將元組 a 解包給 x, y, 其中 y 接收最後元素, 其餘給 x
>>> x                    # 接收其餘元素的 x 型態為 list
[1, 2]
>>> y
3
>>> x, *y=a          # 將元組 a 解包給 x, y, 其中 x 接收第一個元素, 其餘給 y
>>> x                    
1
>>> y                    # 接收其餘元素的 y 型態為 list
[2, 3]
>>> type(y)  
<class 'list'>

元組物件的方法如下表 : 


 tuple 物件方法 說明
 count(i) 傳回 tuple 元素中 i 出現之次數
 index(i) 傳回 tuple 元素中 i 之索引


例如 :

>>> b=('tony','amy','kelly','tony')    
>>> b.count('tony')       # 'tony' 出現 2 次
2
>>> b.index('kelly')       # 'kelly' 的索引是 2
2


5. 集合 (set) :

集合是獨一無二 (不重複) 的物件所構成之可變無序群集 (mutable and unordered collection), 其元素必須是不可變的型別. 數學上的集合是一群元素的組合, 除了元素不能重複外, 還有聯集, 交集, 以及差集運算.


(1). 建立集合物件 : 

Python 的集合字面值 (literal) 是以大括弧運算子 {} 將元素括起來. 每個元素以逗號相隔, 語法格式如下 :

set1={e1, e2, e3, ....}

集合的元素必須是不可變的型別, 因為集合是透過計算元素獨一無二雜之湊值來定位元素, 因此只有 int, float, bool, complex, str, tuple 這六種型別的物件可作為集合之元素, 例如 :

>>> s={123, 12.3, True, False, 7+8j, "abc",  (1, 2, 3)} 
>>> print(s)
{False, True, 'abc', 12.3, (1, 2, 3), 123, (7+8j)}   

至於 list, dict, 與 set 這三種型態資料, 由於它們是可變的, 無法計算雜湊值, 因此不能作為集合的元素, 例如 :

>>> s={[1,2,3]}                 #串列不可作為集合之元素
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'   
>>> s={{'a':1,'b':2}}         #字典不可作為集合之元素
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict' 
>>> s={{1,2,3}}                  #集合不可作為集合之元素
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set' 

集合的長度 (元素個數) 可用內建函數 len() 取得, 例如 :

>>> set1={1, 2, 3, 4, 5} 
>>> len(set1) 
5

除了用大括弧運算子 {} 直接建立集合外, 也可以呼叫內建函數 set() 並傳入一個序列型別物件 (字串, 元組, 或串列) 來建立, 例如 :

>>> set1={123, 456, 'a', 123, 'b', 'a'} 
>>> print(set1) 
{456, 123, 'a', 'b'}
>>> set1=set([123, 456, 'a', 123, 'b', 'a'])   
>>> set1 
{456, 123, 'a', 'b'}

可見集合會將重複的元素刪除以保證每一個元素在集合中是獨一無二的, 如果想從其他群集 (例如元組或串列) 中移除重複的部分, 只要將他們傳給內建函數 set() 即可.

不過對於字串而言, 用 {} 與 set() 所建立的集合是不同的, set() 函數會將字串中的字元分拆開來並去除重複的字元, 而 {} 不會, 例如 : 

>>> set1={'Hello World'} 
>>> print(set1)   
{'Hello World'}
>>> set1=set('Hello World')      
>>> print(set1)    
{'e', ' ', 'H', 'l', 'd', 'r', 'W', 'o'}    #字串會被拆散成字母

集合物件的方法如下表 :


 集合物件的方法 說明
 s1.union(s2) 計算集合 s1 與 s2 之聯集, 傳回新集合
 s1.intersection(s2) 計算集合 s1 與 s2 之交集, 傳回新集合
 s1.difference(s2) 計算差集合 s1-s2, 傳回新集合 (s1-s2 與 s2-s1 不同)
 s1.issubset(s2) 檢查集合 s1 是否為集合 s2 之子集合 (傳回 True/False) 
 s1.issuperset(s2) 檢查集合 s1 是否包含集合 s2 (傳回 True/False) 
 s1.update(s2) 將集合 s2 全部元素加入集合 s1 中 (s1 會改變)
 s1.add(e) 將元素 e 新增至集合 s1 中
 s1.remove(e) 從集合 s1 中移除元素 e (若不存在出現 KeyError)
 s1.copy() 複製集合 s1 傳回一個新集合
 s1.clear() 清空 s1 集合之全部元素 (成為空集合)


注意, 除了有註明傳回值者外, 其餘方法均傳回 None.


(3). 新增集合的元素 :

呼叫 add(e) 方法可將元素 e 加入集合中, 例如 :

>>> set1={'i','a','o','u','e'} 
>>> print(set1)
{'a', 'u', 'i', 'e', 'o'}                 #順序與加入時不同

顯示集合時元素順序與建立時不同, 因為集合是無序的資料結構, 它不會維護元素加入之順序.

注意, 集合與字典都使用大括弧 {} 定義其內容, 而空的大括弧預設是定義一個空字典, 所以不能用 {} 來建立空集合後再呼叫 add() 方法加入元素, 這樣會出現 AttributeError 錯誤, 例如 :

>>> set1={}           #這是一個空字典, 不是空集合
>>> print(set1)   
{}
>>> set1.add('a') 
Traceback (most recent call last): 
  File "<pyshell#49>", line 1, in <module>
    set1.add('a')
AttributeError: 'dict' object has no attribute 'add'
>>> type(set1) 
<class 'dict'>

正確做法是呼叫內建函數 set() 來建立空集合, 例如 :

>>> set1=set()          #建立空集合
>>> type(set1) 
<class 'set'>
>>> print(set1)   
set()
>>> set1.add('a')      #新增元素
>>> set1 
{'a'}

呼叫 update(set) 則可以把另一個集合的全部元素合併到目前的集合中, 並剔除重複的元素, 例如 :

>>> set1={1, 2, 3, 6, 7, 9}   
>>> set2={2, 4, 6, 8, 9}   
>>> set1.update(set2)          #把 set2 的元素加入 set1 中
>>> print(set1)   
{1, 2, 3, 4, 6, 7, 8, 9}

可見加入新集合時會剔除重複的元素 (例如此處之 2, 6, 9).


(2). 集合的聯集運算 : 

呼叫集合物件的 union(set) 方法會傳回此集合與傳入集合之聯集, 它會將兩集合的元素全部合併, 然後剔除重複的元素, 例如 :

>>> set1={1, 2, 3, 6, 7, 9} 
>>> set2={2, 4, 6, 8, 9} 
>>> set3=set1.union(set2)   
>>> print(set3)     
{1, 2, 3, 4, 6, 7, 8, 9} 
>>> set3=set1.union(set2) 
>>> print(set3) 
{1, 2, 3, 4, 6, 7, 8, 9}

可見 union() 具有交換性, 不論哪一個集合在前結果都一樣.


(3). 集合的交集運算 :

呼叫集合物件的 intersection(set) 方法會傳回此集合與傳入集合之交集, 亦即兩集合共同元素所成的集合, 例如 :

>>> set1={1, 2, 3, 6, 7, 9} 
>>> set2={2, 4, 6, 8, 9} 
>>> set3=set1.intersection(set2) 
>>> print(set3)
{9, 2, 6}
>>> set3=set2.intersection(set1) 
>>> print(set3) 
{9, 2, 6}

可見 intersection() 也具有交換性, 不論哪一個集合在前結果都一樣.


(4). 集合的差集運算 :

呼叫集合物件的 difference(set) 方法會傳回此集合與傳入集合之差集, set1.difference(set2) 的意思是將 set1 元素剔除與 set2 交集的部分後之子集合; 而 set2.difference(set1) 則是將 set2 元素剔除與 set1 交集的部分後之子集合, 注意, difference() 沒有交換性, 例如 :

>>> set1={1, 2, 3, 6, 7, 9} 
>>> set2={2, 4, 6, 8, 9} 
>>> set3=set1.difference(set2)    #傳回 set1-set2 元素所成集合
>>> print(set3)
{1, 3, 7}
>>> set1-set2                                 #差集也可以直接用 - 運算子
{1, 3, 7}
>>> set3=set2.difference(set1)   
>>> print(set3) 
{8, 4}
>>> set2-set1                                  #差集也可以直接用 - 運算子
{8, 4}

可見 set1 去除交集 {9, 2, 6 } 後傳回 {1, 3, 7}; 而 set2 去除交集 {9, 2, 6 } 後傳回 {4, 8}, 差集運算沒有交換性, 結果可能是不一樣的.


(5). 集合元素的排序 :

集合是無序的資料結構, 其元素顯示的順序與加入之順序無關, 例如 :

>>> set1={'u','a','e','o','i'} 
>>> print(set1) 
{'a', 'u', 'i', 'e', 'o'}                 #顯示順序並非建立時之順序
>>> for e in set1: 
print(e, end=',')     

a,u,i,e,o,                               #迭代之順序與 print() 相同

如果要在顯示集合元素時進行排序, 可將集合傳給 list() 轉成串列, 再呼叫內建函數 sorted() 排序, 例如 :

>>> set1={'u','a','e','o','i'} 
>>> for e in sorted(list(set1)):   
print(e, end=',')   

a,e,i,o,u,

可見元素已經排序好了.


(6). 複製集合 :

呼叫 copy() 方法會複製集合並傳回為新集合, 例如 :

>>> set1={'u','a','e','o','i'} 
>>> set2=set1.copy() 
>>> print(set2) 
{'i', 'a', 'u', 'e', 'o'}


(7). 刪除集合元素與清空集合 :

呼叫 remove(e) 方法可刪除指定的元素, 但若元素不存在會出現 KeyError; 呼叫 clear() 則會清空全部元素成為空集合, 例如 :

>>> set1 
{'a', 'u', 'i', 'e', 'o'}
>>> set1.remove('e')      #刪除元素 'e'
>>> set1 
{'a', 'u', 'i', 'o'}
>>> set1.remove('e')      #刪除已不存在之元素 'e'
Traceback (most recent call last):
  File "<pyshell#43>", line 1, in <module>
    set1.remove('e')
KeyError: 'e'
>>> set1.clear() 
>>> set1     
set()


6. 字典 (dict) :

字典是由鍵值對 (key-value pair) 構成的可變長度無序群集 (collection), 是一個兩欄多列 (two-columned, multi-row) 的資料結構, 與 Javascript 的關聯式陣列 (associate array), Perl 與 Ruby 的雜湊表 (hash table), 或 Java 的映射 (map) 結構是一樣的, 都是一種透過唯一的鍵 (key) 來對應一個值 (value) 的查找表 (look-up table) 結構.


(1). 建立字典物件 :

字典使用大括弧運算子 {} 來列舉項目 (item), 項目由鍵值對組成, 每一個項目間用逗號隔開, 語法格式如下 :

dict1={key1:value1, key2:value2, key3:value3, ..... }

與集合的元素值不可重複一樣, 字典每一個項目 (item) 的鍵必須是不可變型別且獨一無二不可重複 (unique), 例如 int, float, bool, complex, str, tuple 這六種型別均可作為字典的鍵, 因為這樣才可計算鍵的雜湊值以便關聯其所對應之值, 而值則可以是任何型別的物件 (包括字典), 例如 :

>>> test={0:'zero', 3.14159:'pi', True:1, False:0, 1+1j:'complex 1+1j', (1,1):'tuple (1,1)'} 
>>> print(test)      
{0: 0, 3.14159: 'pi', True: 1, (1+1j): 'complex 1+1j', (1, 1): 'tuple (1,1)'}

通常是用字串做鍵, 例如 :

user1={'account' : 'tony', 'password' : 'blablabla', 'category' : 'admin'} 

為了提高程式可讀性, 字典的項目最好一個項目一列輸入, 由於有大括弧做界線, 所以每一列結尾不需要用倒斜線 \ (有也可以) :

user1={'account' : 'tony',
             'password' : 'blablabla', 
             'category' : 'admin'}

與串列或元組一樣, 存取字典的項目也是是用中括弧運算子 [], 但裡面是使用項目的鍵來取得關聯值, 而不是像串列那樣用數字索引, 例如 :

>>> user1['account'] 
'tony'
>>> user1['password'] 
'blablabla'
>>> user1['category'] 
'admin'

可以先建立一個空字典, 再用中括弧運算子 [] 來初始化鍵 (賦值), 例如 :

>>> user1={}          #建立空陣列 (不是空集合)
>>> user1['account']='tony'   
>>> user1['password']='blablabla'  
>>> user1['category']='admin'       
>>> print(user1)      
{'account': 'tony', 'password': 'blablabla', 'category': 'admin'}

除了用空的大括弧 {} 外, 也可以呼叫內建函數 dict() 來建立空字典 :

>>> user1=dict()  #建立空陣列
>>> print(user1)
{}

此外, 字典還提供了 setdefault(key, value) 方法來初始化字典的鍵, 它會傳回項目之值 (value) :

>>> user1={}          #建立空陣列
>>> user1.setdefault('name', 'tony')   
'tony'
>>> user1.setdefault('password', 'blablabla')  
'blablabla'
>>> user1.setdefault('category', 'admin')   
'admin'
>>> print(user1)  
{'name': 'tony', 'password': 'blablabla', 'category': 'admin'}

因為字典是透過計算鍵的雜湊值 (hash) 來定位其所關聯的值而非索引, 所以顯示字典內容時不一定會依照加入的順序來排序. 字典是無序的 (unordered) 資料結構, 它不會維護項目之間的排列順序.

字典物件的方法如下表 :


 字典物件的方法 說明
 update(字典) 將傳入的鍵值對新增到字典中
 get(鍵) 傳回指定鍵之值, 若鍵不存在會出現錯誤
 keys() 傳回字典所有鍵組成之 dict_keys 物件, 可用 list() 轉成串列
 values()  傳回字典所有值組成之 dict_values 物件, 可用 list() 轉成串列
 items()  傳回字典所有 (鍵, 值) 元組組成之 dict_items 物件, 可轉成串列
 copy()  傳回字典之副本為一新字典
 pop(鍵)  刪除字典中指定之鍵值對, 傳回該鍵之值
 set_default(鍵, 值)  若鍵不存在就新增鍵值對, 



(2). 使用 get() 方法取值 :

字典的鍵必須先初始化 (亦即要先對鍵賦值使其有內容) 才能讀取, 讀取未初始化的鍵會產生 KeyError 錯誤, 例如上面的 user1 並沒有 email 這個鍵, 直接取值會報錯 :

>>> user1['email']    
Traceback (most recent call last): 
  File "<pyshell#51>", line 1, in <module>
    user1['email']
KeyError: 'email'

這問題可以在讀取前先用 in 與 not in 先檢查鍵是否存在來避免 :

>>> if 'email' in user1:   
  print(user1['email'])
else: 
  print('key not found')   

key not found

不過更好的解決辦法是利用字典的 get() 方法傳入鍵來取值, 如果鍵不存在會傳回 None, 不會報錯, 例如 :

>>> user1.get('email') 
>>> type(user1.get('email'))
<class 'NoneType'> 


(3). 新增字典的項目 : 

新增字典項目有兩個方法, 一是直接用中括號運算子 [] 給新鍵賦值, 例如 :

>>> user1['email']='tony@bla.com'
>>> print(user1)   
{'account': 'tony', 'password': 'blablabla', 'category': 'admin', 'email': 'tony@bla.com'} 

第二個方式是呼叫字典物件的 update() 方法, 並傳入新項目鍵值對所組成的字典 (即新增一個字典進去) :

>>> user1.update({'email':'tony@bla.com'})
>>> print(user1)      
{'name': 'tony', 'password': 'blablabla', 'category': 'admin', 'email': 'tony@bla.com'}


(4). 迭代字典的項目 :

迭代 (iteration) 字典項目要用 for in 敘述來拜訪字典中的每一個項目, 但迭代的變數是每個項目的鍵而不是項目本身或值, 例如 :

>>> for k in user1:   
                     print(k) 

account
password
category
email

可見所取得的迭代變數 k 是項目的鍵而非值. 如果迭代變數是要取得項目的值則須呼叫 values() 方法, 它會傳回一個由值串列組成的 dict_values 物件, 例如 :

>>> user1.values()   
dict_values(['tony', 'blablabla', 'admin', 'tony@bla.com']) 
>>> for v in user1.values(): 
                     print(v)   

tony
blablabla
admin
tony@bla.com

如果要存取鍵所對應之值, 則必須用中括號運算子 [k] 或呼叫 get(k) 方法, 例如 :

>>> for k in user1: 
                     print(k + ":" + user1[k])      #或 user1.get(k)

account:tony
password:blablabla
category:admin
email:tony@bla.com

如果要讓迭代的結果排序, 可將字典傳入內建函數 sorted(), 注意, sorted() 是對鍵排序而非對值排序. 例如 :

>>> for k in sorted(user1): 
                     print(k + ":" + user1[k]) 

account:tony
category:admin
email:tony@bla.com
password:blablabla

注意, sorted() 不會改變原始資料 (即傳入之字典) 的順序.

如果要在迭代時同時取得鍵與值, 則可呼叫字典物件的 items() 方法, 它會傳回鍵值對元組組成的 dict_items 物件, 這樣迭代時便能用兩個迭代變數同時取得鍵與值, 這樣就不需要用中括號運算子取得值了, 例如 :

>>> for k,v in user1.items(): 
                     print(k + ":" + v) 

account:tony
password:blablabla
category:admin

如果鍵值對要排序, 同樣可將 items() 的傳回值傳入 sorted() 函數, 例如 :

>>> for k,v in sorted(user1.items()): 
                     print(k + ":" + v) 

account:tony
category:admin
password:blablabla


(5). 刪除字典的項目 : 

欲刪除字典物件中的某個項目可 pop(key) 方法並傳入鍵, 它會傳回該項目之值, 例如 :

>>> print(user1) 
{'account': 'tony', 'password': 'blablabla', 'category': 'admin', 'email': 'tony@bla.com'}
>>> deleted=user1.pop('email')       #刪除鍵=email 的項目
>>> print(user1)   
{'account': 'tony', 'password': 'blablabla', 'category': 'admin'}
>>> print(deleted)                             #傳回被刪除項目之值
tony@bla.com

注意, 刪除不存在的項目 (傳入不存在的鍵) 會產生 KeyError 錯誤 :

>>> user1.pop('email') 
Traceback (most recent call last):
  File "<pyshell#29>", line 1, in <module>
    user1.pop('email')
KeyError: 'email' 

參考 : 

 # Python : How to copy a dictionary | Shallow Copy vs Deep Copy


(7). 設定預設值 :

呼叫 setdefault() 方法可新增一組鍵值對或傳回已存在鍵之值, 當鍵已存在時, 此方法功能與 get() 方法一樣會傳回指定鍵之值 (不管有無傳入預設值); 當鍵不存在時則像 update(), 會將 {鍵: 預設值} 鍵值對加入字典中並傳回其值, 若未傳入預設值則以 None 為值, 例如 :

>>> a={1:'a', 2: 'b'}   
>>> a.setdefault(1)          # 鍵已存在, 傳回其值
'a'  
>>> a.setdefault(3, 'c')    # 鍵不存在, 有傳預設值, 新增鍵值對並傳回其值
'c'  
>>> a   
{1: 'a', 2: 'b', 3: 'c'}             # 新增了 3: 'c' 鍵值對
>>> a.setdefault(3, 'd')    # 鍵已存在, 有傳預設值 (不予理會), 傳回其值
'c'
>>> a   
{1: 'a', 2: 'b', 3: 'c'}             # 鍵 3 之值仍為 'c'
>>> a.setdefault(4)           # 鍵不存在, 未傳預設值, 新增鍵: None 對並傳回其值 None
>>> a
{1: 'a', 2: 'b', 3: 'c', 4: None}   
 

(8). 字典的應用 :

字典這種資料結構的用途之一是可以很方便進行頻率計數統計, 例如下面用字典來統計字串中母音字母 (a, i, u, e, o) 出現次數的範例 :

vowels=['a','i','u','e','o']
word='a silver bullet'
print(word)
found={}
for letter in word:
    if letter in vowels:
        found.setdefault(letter, 0)   #初始化項目值為 0
        found[letter] += 1               #值增量 1
for k,v in sorted(found.items()):
    print(k, 'was found ', v, ' time(s)')

執行結果如下 :

a silver bullet
a was found  1  time(s)
e was found  2  time(s)
i was found  1  time(s)
u was found  1  time(s)


七. 運算子 :

電腦進行運算需有運算元 (operand) 與運算子 (operator). Python 的運算元有各種資料型態如上述, 運算子則有七類 :
  1. 算術運算子 (arithmetic operator) 
  2. 指定運算子 (assignment operator) 
  3. 比較運算子 (comparison operator) 
  4. 邏輯運算子 (logical operator) 
  5. 位元運算子 (bitwise operator) 
  6. 移位運算子 (shifting operator) 
  7. 符號運算子 (symbolic operator) 
以作用的運算元數目區分, 又可分為單元運算子 (unary, 例如正負號) 與二元運算子 (binary).  


1. 算術運算子 (arithmetic operator) : 

算術運算子有七個如下表 :


 算術運算子 說明
 + 加法運算, 例如 a + b
 - 減法運算, 例如 a - b
 * 乘法運算, 例如 a * b
 / 浮點數除法運算, 例如 a / b (傳回 float 型態之浮點數)
 // 整數除法運算, 例如 a // b (傳回商, 為 int 型態之整數)
 % 餘數運算, 例如 a % b
 ** 指數運算, 例如 a ** b (即 a 的 b 次方)


注意 :
  1. '+' 為多用途運算子, 運算元均為數值時為加法運算子; 運算元均為字串時為串接運算子; 做為單元運算子時表示正數, 雖可在正數前面冠 '+' 但不需要,  +123 等同於 123. 但數值與字串不能進行 '+' 運算, 因為 Python 是強型別語言, 不允許隱性 (自動) 型別轉換. 但字串可與數值做 * 運算, 表示將字串重複指定之次數. 
  2. '-' 可做加法運算子, 亦可做單元運算子用來表示負數, 例如 -123. 
  3. 整數除法只傳回相除之商, 小數部分無條件捨去, 不是四捨五入. 
  4. Python 沒有 Java 的 ++ 與 -- 運算子, 如果要遞增與遞減請用 +=1 與 -=1 代替. 
例如 :

>>> -(-100)          #負負得正
100
>>> 10 / 4            #浮點數除法
2.5
>>> 10 // 4           #整數除法傳回商 (略去小數)
2
>>> type(10/4)     #浮點數除法傳回 float 型態
<class 'float'> 
>>> type(10//4)    #整數除法傳回 int 型態
<class 'int'>
>>> 10 % 3           #求餘數
1
>>> 10 ** 3           #冪次 (次方)
1000
>>> 'Hello' + 'World'     #字串串接運算
'HelloWorld'
>>> 123 + 'abc'              #字串與數值不可做 + 運算 (無自動轉型)  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str' 
>>> 'Hello' * 3               #字串重複 3 次
'HelloHelloHello'
>>> 'Hello\n' * 3            #字串重複 3 次 (含跳行字元)
'Hello\nHello\nHello\n'
>>> print('Hello\n' * 3)  #要用 print() 才會有跳行效果
Hello
Hello
Hello


2. 指定運算子 (assignment operator) :

單純指定運算子 = 用來將右邊的運算結果指派給一個變數參考, 例如 :

count=0
msg="Hello World"


(1). 同步指定 : 

Python 支持同時指派多個值到多個對應的變數 (同步指定, simultaneous assignment), 例如 :

a, b, c=1, 2, 3

這功能對於資料交換非常方便, 例如要交換 a, b 兩變數之值可用一個敘述即完成 :

a, b=b, a

這功能在 Javascript, Java 或 C 等語言必須用中間變數交換, 例如 :

var a=1; b=2; 
var tmp=a;  //將 a 暫存在 tmp
a=b;            //a 換成 b 之值
b=tmp;       //b 換成原來的 a 之值


(2). 多重指定 : 

Python 支持將一個值一次指派給多個變數, 例如 : 

a=b=c=d=e=100

此運算式會從右到左 (唯一的例外順序) 將 100 指派給 e. d, c, b, a 五個變數. 

指定運算子還可與算術, 移位, 以及位元運算子結合為複合指定運算子 :


 指定運算子 說明
 = 將右邊的字面值 (literal) 指定給左邊變數, 例如 a=2 
 += 相加指定, 左右相加後再指定給左邊變數, a += b 即 a=a+b
 -= 相減指定, 左右相減後再指定給左邊變數, a -= b 即 a=a-b
 *= 相乘指定, 左右相乘後再指定給左邊變數, a *= b 即 a=a*b
 /= 相除指定, 左右相除後再指定給左邊變數, a /= b 即 a=a/b
 //= 整除指定, 例如 a //= b 即 a=a//b
 %= 餘除指定, 例如 a %= b 即 a=a%b
 **= 指數指定, 例如 a **= b 即 a=a**b
 <<= 左移後指定, a <<= b 即 a=a << b
 >>= 左移後指定, a >>= b 即 a=a >> b
 &= 位元 AND 後指定, a &= b 即 a=a & b
 |= 位元 OR 後指定, a |= b 即 a=a | b
 ^= 位元 XOR 後指定, a ^= b 即 a=a ^ b


注意 : Python 沒有 ++ 與 -- 運算子, 要做遞增與遞減運算需使用相加指定 +=1 與相減指定 -=1 代替.

例如 :

>>> a, b, c=1, 2, 3     #一次指定多個值給對應之多個變數
>>> a
1
>>> b
2
>>> c
3
>>> a += b
>>> a
3


3. 比較運算子 (comparison operator) :

比較運算子又稱關係運算子 (relational operator), 用來比較左右兩個運算元之值的大小關係, 內建資料型別雖均可作為運算元, 但主要用在數值與字串型別, 且兩個運算元必須為同型別, 其運算結果是一個布林值 (True/False), 常用在選擇敘述 if else 與迴圈敘述 while 中控制執行方向或判斷迴圈是否要繼續.


 比較運算子 說明
 == 兩邊值相等時傳回 True, 否則 False
 != 兩邊值不相等時傳回 True, 否則 False
 <> 兩邊值不相等時傳回 True, 否則 False
 > 左邊的值大於右邊的值時傳回 True, 否則 False
 < 左邊的值小於右邊的值時傳回 True, 否則 False
 >= 左邊的值大於或等於右邊的值時傳回 True, 否則 False
 <= 左邊的值小於或等於右邊的值時傳回 True, 否則 False


注意, Python 的不等於可用 != 或 <> 兩種運算子. 其次,  >= 與 <= 運算子的等號都在右邊, =< 與 => 是錯誤的寫法. 

運算元若為字串, 則依據其 Unicode 編碼值由左向右一一比對對應之字元, 直到比出大小或其中一個字串結束為止, 比較長的較大, 呼叫內建函數 ord() 可查詢字元的 Unicode 編碼值. 

例如 :

>>> 1 == 1.0                  #整數與浮點數值相等
True
>>> 1 > 1.0           
False
>>> 1 == 1+0j                 #複數虛部為 0 等於整數
True
>>> True == 1                 #布林值 True 其實就是 1
True
>>> False == 0                #布林值 False 其實就是 0
True
>>> 'hello' == 'hello'        #相同字串
True
>>> 'hello' < 'Hello'     #大寫字母 ASCII 編碼較小
False
>>> 'hello' > 'Hello'     #大寫字母 ASCII 編碼較小
True
>>> 'a' < 'b'      #英文字母 ASCII 碼 'a' 比 'b' 小
True
>>> ord('a')
97
>>> ord('b')
98
>>> 'a' < 'ab'    #前面字元相同, 長度較長者較大
True
>>> 'a' < '文'    #英文的 Unicode 一定小於非英文字
True
>>> '文' < '文學'    #前面字元相同, 長度較長者較大
True

兩個數值關係運算式若形成範圍可以寫成範圍關係運算式, 例如體溫介於 36 與 37.5 為正常 :

if temp >=36 and temp < 37.5:           # 以邏輯 and 運算子串聯兩個關係式
    print("體溫正常")
else: 
    print("體溫異常")

可以改寫為如下之範圍關係運算式 :

if 36 <= temp <37.5:        # 範圍關係運算式
    print("體溫正常")
else: 
    print("體溫異常")


4. 邏輯運算子 :   

邏輯運算子的運算元均為布林值, 用來執行運算元的 and, or, 與 not 三種邏輯運算, 其中 not 為單元運算子, 只有一個運算元 :


 邏輯運算子 說明
 and 兩個運算元均為 True 時才傳回 True, 否則 False
 or 兩個運算元均為 False 時才傳回 False, 否則 True
 not 運算元值為 True 傳回 False, 否則 True (反相)


例如 :

>>> a=True
>>> b=True
>>> a and b     #兩運算元均為 True 則為 True
True
>>> a or b
True
>>> not a        #真變假
False
>>> not b
False


5. 位元運算子 (bitwise operator) :

位元運算子是將運算元以二進位形式展開後逐位元進行 0 與 1 的邏輯運算, Python 有四個位元運算子 :


 位元運算子 說明
 ~ 位元否定 (not) 運算 (即 1 的補數), ~1 為 0, 而 ~0 為 1
 & 位元且 (and) 運算, 只有 1&1 為 1, 其餘為 0
 |  位元或 (or) 運算, 只要有一個運算元為 1 即得 1
 ^ 位元互斥或 (xor) 運算, 只要 1^0 與 0^1 得 1, 其餘得 0


例如 :

>>> ~7
-8
>>> bin(7)      #轉成二進位
'0b111'
>>> bin(~7)    #取 1 的補數再轉成二進位
'-0b1000'
>>> 3 & 8       #0011 & 1000=0000 (0)
0
>>> 9 & 8       #1001 & 1000=1000 (8)
8
>>> 5 & 12     #0101 & 1100=0100 (4)
4
>>> 5 | 12       #0101 | 1100=1101 (13)
13
>>> 5 ^ 12      #0101 ^ 1100=1001 (9)
9


6. 移位運算子 (shifting operator) :

移位運算子是對運算元的二進位形式進行向左或向右移動一個位元的動作 : 


 移位運算子 說明
 >> 右移一個位元, 左方補 0, 相當於除以 2
 << 左移一個位元, 右方補 0, 相當於乘以 2


例如 :

>>> a=3  
>>> bin(a)          #傳回二進位形式
'0b11'
>>> b=a << 1   
>>> b   
6
>>> bin(b)  
'0b110'
>>> b >> 1   
3


7. 符號運算子 (symbolic operator) :

Python 使用如下符號表示資料結構, 也是一種運算子 :


 符號 說明
 () 小括號 定義 tuple (元組/序對), 函數參數, 或優先處理之運算式
 [] 中括號 定義 list (串列), 或索引
 {} 大括號 定義 dict (字典) 或 set (集合) 
 ,   逗號 用來分隔變數, 運算式, 參數, 或容器型別的元素
 :   冒號  用來分隔字典之鍵值對, 分支與迴圈指令與其區塊
 .   小數點 存取物件之屬性或方法
 ;   分號 分隔多個敘述


注意逗號在元組中的特殊作用, 元組如果只有一個元素時必須在後面添加一個逗號, 否則不會是一個 tuple, 例如 :

>>> i=(5)   
>>> type(i)   
<class 'int'>    
>>> i=(5,)    
>>> type(i)    
<class 'tuple'> 

以上的運算子是有優先順序的, 如下表所示 :


 運算子優先順序 說明
 1  () 巢狀多層時內層優先 (由內而外)
 2  ** 冪 (次方)
 3  ~ 位元運算子 (NOT, 否定)
 4  +,  - 正負號
 5  *,  /,  //,  % 乘 *, 除 /, 整數除法 //, 求餘數 %
 6  +,  - 加減運算
 7  <<, >> 位元運算子 : 左移 <<, 右移 >>
 8  & 位元運算子 AND
 9  ^ 位元運算子 XOR
 10  |  位元運算子 OR
 11  in, not in, is, is not, <,  <=, >,  >=,  !=, == 成員 (in, not in), 識別 (is. is not), 與關係運算子
 12  not 邏輯運算 (否定)
 13  and  邏輯運算 (且)
 14  or 邏輯運算 (或)


Python 運算式中含有多個運算子時, 基本上都是由左向右依照上面的優先順序運算 (可用小括號改變先後順序), 唯一的例外是多重指定運算式, 它是由右到左運算, 例如 x=y=z=0 會先執行 z=0, 然後 y=0, 最後 x=0. 

四則運算遵循普通數學裡的先乘除後加減原則, 例如 :

>>> 1+2*3/4      #先算 2*3/4 得 1.5 再與 1 相加得 2.5
2.5
>>> 1*2+3/4      #先算 1*2 得 2 與 3/4 得 0.75, 再相加得 2.75
2.75

注意, 排序 4 的乘除求餘數 (* / // %), 排序 5 的加減, 以及排序 6 的關係運算中的運算子優先順序一樣, 同時出現時運算式會依先來後到原則由左向右運算, 例如 :

>>> 100//5*3%7    #100 先整除 5 得 20 再乘 3 得 60 再除 7 取餘數得 4
4
>>> 40%6*5/4       #40 除 6 取餘數得 4 再乘 5 得 20 再除以 4 得 5
5.0

為了避免邏輯錯誤, 運算式應該用第一優先的小括弧明確訂出優先順序.

算術運算式是由左向右依據運算子優先順序 (參考下面的順序表) 依序計算, 但可用

除了上面的運算子外, 還有如下表七組特殊符號 :


 符號 說明
 () 小括號 定義 tuple (元組/序對), 函數參數, 或優先處理之運算式
 [] 中括號 定義 list (串列), 或索引
 {} 大括號 定義 dict (字典) 或 set (集合) 
 ,   逗號 用來分隔變數, 運算式, 參數, 或容器型別的元素
 :   冒號  用來分隔字典之鍵值對, 分支與迴圈指令與其區塊
 .   小數點 存取物件之屬性或方法
 ;   分號 分隔多個敘述


這些運算子用法已在上面資料型態中描述. 

沒有留言 :