2020年2月29日 星期六

電腦與手機鏡頭視訊透過 WiFi 連線的方法

今天在 Youtube 看到有人分享如何將手機鏡頭的視訊透過 WiFi 連線到電腦上的教學, 對於製作動手做的視訊教學很有用 :

如何把手機當電腦視訊鏡頭使用教學- DroidCam




DroidCam - 讓你把手機變成視訊鏡頭




這個方法需要在電腦端與手機端分別下載軟體才行, 電腦端要到下列網址下載安裝 DroidCam Client 這軟體 :

https://www.dev47apps.com/droidcam/windows/

此軟體經 VirusTotal 掃描為安全無毒軟體, 我安裝的是最新 6.1 版, 安裝時會跳出下面視窗, 按 "安裝" 鈕但取消 "永遠信任" 項即可.

其次到手機搜尋 "DroidCam" 下載安裝其中的 DroidCam Wireless Webcam 這個 App, 開啟 WiFi 連線電腦所連線的同一基地台, 畫面會顯示手機獲得之 內網 IP (此處為 192.168.43.29) 與 4747 埠, 此 App 固定用 4747 埠通訊 :




然後在電腦端開啟 DroidCam 軟體, 輸入手機之 IP 按 Start 鈕即可 :




連線建立後即在視窗中間嵌入手機鏡頭視訊畫面 :




按左下角的第二個 "Pop Out Video Preview" 按鈕會開啟單獨的視訊視窗.

有了這個工具就可以把淘汰下來的堪用手機拿來做為視訊教學鏡頭了. 等這陣子 AWS 認證忙完後就來開始製作 MicroPython on ESP32 的實驗視頻教學影片製作吧!

參考 :

利用舊安卓手機和DroidCam做台式機攝像頭

好書 : D3 for the impatient

今天找到一本 D3.js 的好書 :

D3.js for the Impatient


Source : 天瓏


D3.js 學習曲線較陡, 要花蠻多時間學習, 此書書名 "for the impatient" 顧名思義就是要讓學習者能快速上手, 是 D3 初學者不可多得的好書.

製作 Win10 1909 版系統映像檔

上週我的 ACER Swift5 筆電升版為 1909 版, 但一直沒時間製作新的映像檔, 昨天終於把 C 碟下的檔案備份到隨身硬碟去, 今天傍晚回到鄉下順利製作了映像檔, 由於高達 62GB, 所以存放於 WD 那顆 2T 硬碟裡.

在做映像檔之前要先做的清空動作包括 :
  • 下載檔案
  • 文件, 圖片, 影片下的檔案
  • 資源回收桶
  • 瀏覽器隱私權與安全性 (清除瀏覽資料)
  • 檔案總管之快速存取
在開始備份之前, 先查看檔案總管本機, 記下 D 碟可用空間, 以便計算映像檔用掉多少空間.


1. 開啟控制台/系統, 點左下角的 "安全性與維護" : 




2. 點左下角的 "檔案歷程記錄" (如未開啟要先開啟) : 




3. 點左下角的 "系統映像檔備份" : 




4. 點選要備份到 D 碟 :





5. 開始備份 :




映像檔製作完成後, 再查看檔案總管本機 D 碟空間, 計算映像檔共用掉 62GB 空間, 這裡面還包括了 OneDrive 的 12GB 與 DropBox 的 2GB, 故實際上 Win10 本身應該 45~50 GB 左右.

參考 :

升級 Win10 並建立系統映像檔
回復舊版 Win10 停止自動更新並建立修復隨身碟

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

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'


四. 變數與常數 :

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

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

Python 的保留字總共有 35 個, 可透過內建模組 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

使用保留字當變數名稱會出現語法錯誤, 例如 as (但 As 或 AS, aS 則可) :

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


五. 資料型態 :

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 均可改變.

容器型別物件 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, 其長度雖然是無限, 但實際上受到硬體 (記憶體) 限制, 並非無限大. 對於 64-bit 系統而言, 整數的範圍為 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807. 此資訊可利用內建系統模組 sys 的 maxsize 屬性查得 (最小整數是 -sys.maxsize-1), 在 Win 10 64-bit i5 系統上查詢結果如下 :

>>> type(1)                  #整數的型別名稱為 int
<class 'int'> 
>>> import sys             #引入 sys 模組
>>> sys.maxsize           #顯示 maxsize 最大整數屬性
9223372036854775807
>>> -sys.maxsize-1   
-9223372036854775808 

整數可用十進位 (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


(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) : 

Python 沒有字元型別, 字元是以字串來表示.


(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' 字元隔開, 例如 :

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

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


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

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

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

此例 Hello 與 World 中間有一個跳行自元 "\n", 所以加起來是 11 個字元.


(4). 字串的運算 : 

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

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

一個字串可用乘法運算子 "*" 與一個正整數相乘而複製, 若乘以 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) :




(6). 字串方法 :

字串物件提供許多方法參考 :

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


3. 串列 (list) : 

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


(1). 建立串列物件 :

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

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

與 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

除了使用字面值來建立串列外, 還可以呼叫內建函數 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']


串列物件的方法如下表 :


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


(1). 新增串列元素 :


append([]) : 會添加空陣列於結尾
extend([]) : 不會添加空陣列於結尾


4. 元組 (tuple) : 

元組是不可變有序的群集 (unmutable and ordered collection), 它與串列一樣都是有序的序列結構, 差別僅僅是元組的元素不可變而已.



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) 資料結構, 它不會維護項目之間的排列順序.

字典物件的方法如下表 :





(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' 


(7). 字典的應用 :

字典這種資料結構的用途之一是可以很方便進行頻率計數統計, 例如下面用字典來統計字串中母音字母 (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) 

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) :

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

a, b, c=1, 2, 3

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


 指定運算子 說明
 = 將右邊的字面值 (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 編碼值由左向右一一比對對應之字元, 直到比出大小或其中一個字串結束為止, 比較長的較大. 英數字串的 Unicode 即為其 ASCII 編碼.

例如 :

>>> 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
>>> 'a' < 'ab'    #前面字元相同, 長度較長者較大
True
>>> 'a' < '文'    #英文的 Unicode 一定小於非英文字
True
>>> '文' < '文學'    #前面字元相同, 長度較長者較大
True


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 為 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)
'-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) :

移位

 移位運算子 說明
 >> 右移一個位元
 << 左移一個位元




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

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


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


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

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

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


 運算子優先順序 說明
 1  () 巢狀多層時內層優先 (由內而外)
 2  ** 冪 (次方)
 3  +  - 正負號
 4  *  /  //  % 乘, 除, 求餘數
 5  +  - 加減運算
 6  <  <= >  >=  !=  == 關係運算 
 7  not 邏輯運算 (否定)
 8  and  邏輯運算 (且)
 9  or 邏輯運算 (或)


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

>>> 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 (集合) 
 ,   逗號 用來分隔變數, 運算式, 參數, 或容器型別的元素
 :   冒號  用來分隔字典之鍵值對, 分支與迴圈指令與其區塊
 .   小數點 存取物件之屬性或方法
 ;   分號 分隔多個敘述



七. 流程控制 :

結構化程式設計主要包括下列三種語法結構 :
  • 循序 (sequential)
  • 分支 (branch)
  • 反覆 (repeat)
其中分支又稱為選擇 (selection) 結構 , 反覆又稱為迭代 (iteration) 或迴圈 (loop), 程式由上而下循序執行, 但遇到反覆或分支指令時就會停留或改變執行方向, 故分支與反覆屬於流程控制指令.


1. 分支 (branch) : 

分支使用條件式控制程式的執行方向, 語法如下 :

if 條件式1 :
    分支區塊1
[elif 條件式2 :
    分支區塊2]
[elif 條件式3 :
    分支區塊3]
....
[else :
    分支敘述]

中括號表示 elif 與 else 是可有可無的, 只有 if 時為單一分支; 只有 if 與 else為雙分支; 含有 if, 一個以上 elif, 以及 else 為多分支. Python 沒有 switch case 這種多分支指令, 只能使用 if elif else 指令來寫. 條件式會傳回 True 或 False, 可以是邏輯或比較運算式, 注意, 這些條件式在邏輯上必須是互斥 (exclusive), 否則必須將優先項目放在前面.

程式會由上而下依序判斷 if 與 elif 中的條件式, 當條件式為真時即執行縮排之分支區塊, 如果 if 與 elif 的條件式全部都不滿足才會執行 else 的分支區塊. 如果分支區塊裡面只有一個敘述, 可以直接寫在 if/elif/else 後面, 不須跳行縮排, 例如下面判斷成績等級的範例 :

score=int(input('請輸入成績:'))
if score >= 90 : print('優等')
elif score >= 80 : print('甲等')
elif score >= 70 : print('乙等')
elif score >= 60 : print('丙等')
else : print('丁等')

下列範例為金融商管應用上, 將景氣信號綜合分數轉換成景氣對策燈號 :

score=int(input('請輸入景氣對策信號綜合分數:'))
if score >= 38 : print('紅燈')
elif score >= 32 : print('黃紅燈')
elif score >= 23 : print('綠燈')
elif score >= 17 : print('黃藍燈')
else : print('藍燈')

這兩個範例的條件式並沒有互斥, 但把分數高的放在前面優先判斷 (若使用小於則分數高的要放後面), 所以結果仍然正確.


2. 迴圈 (loop) : 

迴圈用來處理需重複執行的迭代工作 (iteration), 可減少程式的複雜度, 在語法上又稱為反覆結構, 主要由迴圈條件 (condition) 與迴圈主體 (即要重複執行之程式碼段) 構成. 迴圈條件用來控制迴圈結束之時機, 若設定不當將使程式進入無窮迴圈狀態.

Python 的迴圈指令有兩種 :
  • for 迴圈 : 進入迴圈前迴圈次數已確定
  • while 迴圈 : 進入迴圈前迴圈次數不確定
for 迴圈通常與內建函數 range() 配合進行整數循序迭代; 而 while 迴圈則配合條件式來控制迭代是否繼續.


(1). for 迴圈 : 

for 迴圈用在進入迴圈前迴圈次數已確定之場合, 其語法如下 :

for 迭代變數 in 迭代器 : 
    迴圈主體
[else : 
    跳出執行運算式]

其中迭代器可以是下列容器 :
  • 字串 (string) : 迭代變數值為字串中的字元
  • 元組 (tuple) : 迭代變數值為元素
  • 串列 (list) : 迭代變數值為元素
  • 字典 (dict) : 迭代變數值為鍵 (key)
最常用的迭代器為使用內建函數 range(start, end, step=1) 來產生的整數串列, 此函數會傳回起始值為 start, 終止值為 end-1 (即開區間), 間隔為 step (預設=1) 的整數串列. else 區塊可有可無, 用途是在完成迴圈時執行特定工作 (但以 break 跳出迴圈不會執行 else 內之區塊), 參考 :

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

例如下列範例為利用迴圈計算正整數數列 1, 2, 3, ... 100 之和 :

sum=0
for i in range(1, 101) :      #range() 傳回 1~100 整數串列
    sum += i
else :
    print("1~%d 正整數數列和=%d" % (100, sum))  #結果為 5050

此例用 range(1, 101) 傳回的 [1, 2, 3, ... 100] 整數串列作為迭代器來累加等差數列, 並在跳出迴圈時輸出級數和結果.

迭代器為字典時, 迭代變數為字典的鍵 (不是值), 例如 :

fruits={'蘋果':'apple','西瓜':'watermelon','香蕉':'banana'}
for fruit in fruits :
    print('%s : %s' % (fruit, fruits[fruit]))

此例迭代變數 fruit 為字典變數 fruits 之鍵, 輸出結果為 :

蘋果 : apple
西瓜 : watermelon
香蕉 : banana

兩層以上迴圈稱為巢狀迴圈 (nesting loop), 需注意內外層迭代變數之不同, 例如用雙層迴圈列印九九乘法表 :

for i in range(1,10):
    for j in range(1,10):
        print('%d*%d=%2d' %(i, j, i*j), end=' ')  #end 為一空格
    print('\n')  #跳行

此程式外層迭代變數為 i, 內層迭代變數為 j, 內建函數 range(1,10) 會傳回 1~9 的整數串列作為迭代器, 利用 print() 函數控制列印格式, 結果如下 :

1*1= 1 1*2= 2 1*3= 3 1*4= 4 1*5= 5 1*6= 6 1*7= 7 1*8= 8 1*9= 9
2*1= 2 2*2= 4 2*3= 6 2*4= 8 2*5=10 2*6=12 2*7=14 2*8=16 2*9=18
3*1= 3 3*2= 6 3*3= 9 3*4=12 3*5=15 3*6=18 3*7=21 3*8=24 3*9=27
4*1= 4 4*2= 8 4*3=12 4*4=16 4*5=20 4*6=24 4*7=28 4*8=32 4*9=36
5*1= 5 5*2=10 5*3=15 5*4=20 5*5=25 5*6=30 5*7=35 5*8=40 5*9=45
6*1= 6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 6*7=42 6*8=48 6*9=54
7*1= 7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 7*8=56 7*9=63
8*1= 8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 8*9=72
9*1= 9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81


(2). while 迴圈 : 

while 迴圈用在進入迴圈前迴圈次數已確定之場合, 其語法如下 :

while (條件式) :
    迴圈主體
[else:
    跳出執行運算式]

在條件式為真時會反覆執行迴圈主體中的程式碼區塊, 否則跳出迴圈, 跳出時執行 else 區塊  (但以 break 跳出迴圈不會執行 else 內之區塊). 注意, 迴圈主體中應進行條件變更, 避免成為無窮迴圈, 例如 :

i=0
while (i<6) :
    print(i)
    i=i+1

此程式變數 i 有一個初始值 0, 進入迴圈時先用條件式 i< 6 判斷真假, 為真才執行迴圈主體內之敘述, 迴圈內對 i 增量進行條件變更, 重複執行迴圈值到條件式為假時跳出迴圈.


(3). 迴圈中斷指令 break 與 continue :

不論是 for 或 while 迴圈, 在迴圈內部還可以在滿足特定條件式時利用 break 或 continue 指令來中斷迴圈, 不過 break 與 continue 中斷的作用不同, 執行 break 指令會跳出迴圈; 而執行 continue 指令則停止目前迴圈, 忽略後面到迴圈結尾的指令, 回到迴圈開始的地方直接進入下一個迴圈.

在 "Effective Python" 這本書裡, 作者建議在 for 或 while 迴圈中最好不要使用 else 區塊, 因為其語意容易與 if else 或 try except else 中的 else 混淆而使程式解讀或維護造成困擾.


(4). 無窮迴圈 : 

無窮迴圈會持續不斷執行迴圈內的敘述, 直到使用者按下 CTRL+C 強制中斷程序為止, 除非必要, 否則使用迴圈時應避免使其成為無窮迴圈.

Python 的無窮迴圈可用 for 或 while 迴圈 :


while (true) :
    敘述


(1). while 無窮迴圈 :

迴圈條件式之值永遠為真的迴圈稱為無窮迴圈, 此種程序不會停止, 只能透過 CTRL+C 強制終止程序. 下面為參考 "Creative Coding in Python" 一書中的猜數字範例修改而來 :

secret_number=87
n=int(input('輸入 1~100 的數字:'))
while not (n==secret_number):
    if n > secret_number:
        print('您猜的數字太大了!')
    else:
        print('您猜的數字太小了!')
    n=int(input('再輸入 1~100 的數字:'))
print('您猜對了')

下面範例綜合使用分支與迴圈來檢查身分證字號是否正確, 台灣身分證共 10 個字元, 格式如下 (例如 A123456789) :


 英文字母 N1 N2 N3 N4 N5 N6 N7 N8 N9


第一個字元為英文字母, 代表出生地, 其他 9 個字元均為數字, 第一個數字 N1 代表性別, 1 表示男性, 2 表示女性, 其餘 8 個 N2~N9 是身分證號碼, 檢查身分證正確性的演算法是先將英文字母依據下列對應表轉成10~35 的數值 (出生地為舊行政區劃) :


 字母 數值 (X1X2) 出生地
 A 10 台北市
 B 11 台中市
 C 12 基隆市
 D 13 台南市
 E 14 高雄市
 F 15 台北縣
 G 16 宜蘭縣
 H 17 桃園縣
 I 34 嘉義市
 J 18 新竹縣
 K 19 苗栗縣
 L 20 台中市
 M 21 南投縣
 N 22 彰化縣
 O 35 新竹市
 P 23 雲林縣
 Q 24 嘉義縣
 R 25 台南縣
 S 26 高雄縣
 T 27 屏東縣
 U 28 花蓮縣
 V 29 台東縣
 W 32 金門縣
 X 30 澎湖縣
 Y 31 陽明山
 Z 33 連江縣


驗證身分證字號的演算法是將英文字母對應數值 (X1X2) 的個位數 X2 乘以 9 加上十位數 X1, 然後將其餘數字由左到右分別乘以 8, 7, 6, 5, ...., 1, 1, 然後全部加總起來 :

S=X1 + X2*9 + N1*8 + N2*7 + N3*6 + N4*5 + N5*4 + N6*3 + N7*2 + N8*1 + N9

此總和若能被 10 整除即為合法之身分證字號, 程式如下 :

id=input('請輸入身分證字號:')
id=id.upper()   #轉成大寫
x1x2={'A':'10','B':'11','C':'12','D':'13','E':'14','F':'15','G':'16',
'H':'17','I':'34','J':'18','K':'19','L':'20','M':'21','N':'22','O':'35',
'P':'23','Q':'24','R':'25','S':'26','T':'27','U':'28','V':'29','W':'32',
'X':'30','Y':'31','Z':'33'}   #英文字母數值對應表
N=len(id)  #身分證字號長度
correct_format=(N==10) and (id[0].isalpha()) and (id[1:N].isdigit())
if correct_format:  #格式正確才進行驗證
    acode=x1x2.get(id[0])   #取得英文字母代碼
    x1=int(acode[0])        #取得字母代碼十位數
    x2=int(acode[1])        #取得字母代碼個位數
    i=1     #id[] 數字索引起始值 id[0] 為字母
    sum=0   #總和初始值=0
    for n in range(8, 0, -1):         #迭代 n=8,7,6,5,4,3,2,1
        sum=sum + int(id[i])* n       #計算 N1 到 N8 加權之和
        i += 1                        #索引增量
    sum=x1 + x2*9 + sum + int(id[i])  #驗證碼總和 (末項為 N9)
    print("總和=", sum)
    remainder=sum % 10
    if remainder==0:
        print("身分證字號正確")
    else:
        print("身分證字號不正確")
else:
    print("身分證字號格式 : 1個英文字母+9個阿拉伯數字")

執行結果如下 :

請輸入身分證字號:a123456789
總和= 130
身分證字號正確
請輸入身分證字號:s121905222
總和= 152
身分證字號不正確


參考 :

python 程式設計50題測試範例-15
檢查台灣身份證號碼

~~進行中~~