# 小學PYTHON課程 ## 課程分配 * 第一次 Python環境安裝,HackMD網路筆記本使用 我的第一支Python程式 變數的認識、for迴圈使用:九九乘法表 註解的使用 輸入與if判斷式:找出質因數 計算功能、運算子 * 第二次 資料型態與轉換、字串切割 串列List與資料切割 字典與資料切割 * 第三次 物件導向與import:簡易繪圖 簡易人臉辨識(依學生進度) ## 環境架設 * 用Anaconda當作開發環境,優點是整合了Package安裝、Environment建設及寫程式的環境 * 到官網下載 https://www.anaconda.com 最新版Anaconda安裝 * jupyter:寫phthon程式 * 點選執行 * 出現cmd視窗及網頁,若未自動執行網頁,在cmd視窗中複製網址到瀏覽器貼上執行 * 網頁會出現本機所有檔案,可以在這裡管理檔案、新增phthon程式,執行python程式 ## 免費網路python程式資源 * Python3 https://www.youtube.com/watch?v=1PC3etgLwVc&list=PLXO45tsB95cIRP5gCi8AlYwQ1uFO2aQBw * Python3 docs https://docs.python.org/zh-cn/3.7/library/index.html * Solo Learn https://www.sololearn.com/Course/Python/ * Udemy https://www.udemy.com/pythonforbeginnersintro/ * Python https://happycoder.org/2016/12/31/python101-tutorial/ --- ## 注意 * 英文大小寫不同 ## 註解 ``` Python # 單行註解 """ 中間可以放 很多行的註解 """ ''' 中間可以放 很多行的註解 ''' ``` ## 計算 ``` python a ** c # a的c次方 x = 9 % 5 #9除以5的餘數指定給x x = 9 // 5 #將9除以5的整數部分指定給x,此範例x=1 ``` * 計算的優先順序(以下方順序由上到下) * () * 次方 * 乘法 * 除法 * 餘數% * 整數// * 加法 * 減法 * 指派運算子 ```python += 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次方 ``` ## 變數 * 不需要事先宣告 * 名稱有大小寫之分 * 不可使用系統保留字或內建函數名稱,若使用了內建函數名稱當作變數,程式不會出錯,但是會讓原先的函數功能喪失 * 變數名稱內不可有, $ 以及不可由數字開頭 * 變數名稱可以用中文 * 刪除變數 ```del 變數名稱 ``` ## 資料型態 * 先宣告變數,但未指定型態 ``` python var = None # 代表尚未設定型態,此時用type(var),會顯示NoneType ``` * 布林:True False ``` python x = 1 z = x == 1 執行後 z = True ``` ### 字串: * 變數名稱. 按tab鍵可以叫出快顯功能表(記得下參數) * https://docs.python.org/3.4/library/string.html * 逸出字元 * \\ = 反斜線 * \' = 單引號 * \" = 雙引號 * \a = 鈴聲 * \b = 倒退 * \f = 換頁 * \n = 換列 * \r = 游標移至最左邊 * \t = 水平跳格(TAB鍵效果) * \v = 垂直定位 * \x = 16進位表示 * \N{id} = Unicode資料庫ID * \uhhhh = Unicode 16位元之十六進位值 * \Uhhhh = Unicode 32位元之十六進位值 * \xhh = 十六進位值 * \o = 八進位表示 * \0 = null (是null字元,python不像C用null字元來判斷字串結束) * 字串與整數相乘代表重複該字串的整數次數 * 字串前加上r可以取消字串中的逸出字元 ``` x = r'王小\n明' ``` * 把字串當串列用時,更改內容的串列函式不可用,其他的串列函示皆可套用,要想改變內容,就要將字串轉換成串列 ``` python y = "hello" y = 'hello' z = '''hello word''' z = """hello word""" #用三個引號包起來的方式可以換行,會加入\n這個字元 z[0] #拿出第1個字母,把字串當串列用,但不能更改內容元素 z[-1] #拿出最後1個字母 #-號代表從後面數過來 z.count #變數後面加上 . 再按tab鍵,可以呼叫已經寫好的應用功能 y + z #+將多個字串連起來 #另外可以用print("abc", "def", type(var))這種方式,每一個參數中會有一個空格 str() #強制轉換為字串 chr() #傳回數字參數的代表字元,用ASCII值,如chr(97)顯示a ord() #傳回字元參數的Unicode值,參數只能放1個字元 lower() #轉換成小寫字 upper() #轉成大寫字 title() #轉成第一個字母大寫,其他是小寫 rstrip() #刪除字串尾端多餘的空白 lstrip() #刪除字串開始多餘的空白 strip() #刪除字串頭尾兩端多餘的空白 nli = list('hello') #將字串轉換成串列 split() #將字串以空格為分隔符號,將字串內容拆成串列 ``` ### 數字 * 整數與浮點數會自動判斷 * 可以用2進位方式表示,0b開頭的表示2進位 ``` python bin() #轉換成2進位 ``` * 可以用8進位方式表示,0o開頭的表示8進位 ``` python oct() #轉換成8進位 ``` * 可以用8進位方式表示,0x開頭的表示8進位 ``` python hex() #轉換成8進位 ``` ``` python int() #將資料型態強制轉換成整數,範例int(x) float() #將資料型態強制轉換成浮點數 nb = list(range(3)) #nb = [0,1,2] ``` ``` python type() #列出變數的型態 abs() #計算絕對值 pow(x,y) #返回x的y次方 round() #進位,如果最左邊的數是奇數則使用四捨五入,如果是偶數則使用五捨六入,如round(1.5)=2 round(2.5)=2 round(5.364, 1) #四捨五入進位到小數第1位 ``` ### 列表 串列 list * 變數名稱. 按tab鍵可以叫出快顯功能表(記得下參數) * 元素可以同時儲放不同型態的資料,或者另一個list * 索引值-1代表最後一個元素,-2代表最後第2個元素 * 將兩個列表用+等於將兩個列表合併為一個列表,後面列表的元素就會接在前面列表元素的後面 * 串列內可以有字典元素 ``` python li = [1, 2, 3, 'stn'] liz = [1,2,3, [10,20,'str']] li = li * 3 #將串列元素重複三次 li1 = li2 + li3 #將li2和li3串列合成一個li1的串列,也可以用 li2+=li3, li = [] #宣告一個空的列表 li1 = li2 #將li1串列的位址拷貝給li2,所以更動li2時也會影響li1 li1 = li2[:] #如果要拷貝內容而非位址用此方法,這樣兩個串列之間不會互相影響 li[0] #拿出第1個值 li[3][2] #取出多元串列得值 li[start:end] #讀取從索引start到(end-1)索引的元素 li[0:4] #從第0個開始拿四個值 li[:3] #從頭取3個值 li[2:] #從第3個值(因為開頭是0)取到最後 li[-3:] #拿出後面三個值 li[:] #取得所有元素 li[2:4:2] #從第二個取到第四個,區間為2個 v1,v2,v3,v4=li #將li的四個元素依序指定給v1 v2 v3 v4,這樣寫法較有效率 li[2] = 4 #修改第3個元素的值 del li[3] #刪除li索引值3的元素 del li[2:4] #也可以和[:]或[::step]的方法結合 del li #刪除整個列表 #使用del刪除不會有回傳值,如果要回傳值,需使用pop() vname = li.pop(2) #參數是索引值,如果空白代表刪除最後一個元素 li.remove("car") #刪除元素內容為car的 max(li) #列表中的最大值,但有非數字元素會出錯 min(li) #列表中的最小值,但有非數字元素會出錯 sum(li) #列表中所有元素的總和,但有非數字元素會出錯 len(li) #列表中有多少元素 li.append(111) #把111接在li的最後一個元素 #參數也可以放另一個串列 li1.extend(li2) #將li2的元素都併入li1中,變成一個串列,但是在anaconda中未實驗成功 li.insert(索引, 元素內容) #插入新的元素,原有元素會向後排 li = li.astype(str) #把li轉換成字串型態 pd.to_numeric(s, errors='coerce') #轉換成數字,s是輸入項,errors= 'coerce'是指說轉換有誤,直接將值換成NaN,不要顯示出錯誤 li.reverse() #反轉串列 li[::-1] #將串列顛倒排序 li.sort() #將串列由小到大排序,如果要從大到小,參數加上reverse=True #排列英文時,建議先全改成大寫或小寫比較不會有錯 new_list = sorted(li) #將li串列由小到大排序,並將結果指給new_list串列,原串列不動 #如果要從大到小,加上參數 sorted(li, reverse=True) li.index("abc") #abc在串列中第一次出現的索引值 li.count("abc") #abc在串列中出現的次數 char.join(li) #將li串列全部串再一起,char是串接的字元 #但是要注意格式,char是字串,但串列內的數字和文字無法直接合併 tuple(li) #將list資料轉換成tuple concat([序列1,序列2], axis = 1) #將兩個序列合併,axis式合併方向,可以省略,預設是0,將序列2接在序列1的下方,如果是1,就是將序列2接在序列1的右方 #只能合併序列或DataFrame的資料 ``` ### enumerate ``` python #將list tuple set等數值形式改用計數值與元素的方式傳回,傳回的數據稱為enumerate物件 #語法 obj = enumerate(iterable [,start = 0]),省略start設定,預設值是0 li1 = ["bask", "ask"] li2 = enumerate(li1) li2 = enumerate(li1, start = 10) #將初始值設為10 for i,z in li2: print(i,z) ``` ### tuple 元組 * 與list串列相同,但是不可以更動元素值與元素個數 * 若要更新元素內容,需重新定義整個tuple * 優點是資料更安全,也佔用較少的系統資源,增加執行速度 ``` python name_tuple = (元素1, 元素2, 元素3, ....) #如果元素只有1個,要加上, name_tuple = (元素1,) #讀取元素(索引值從0開始) name_tuple[2] #讀取所有元素用 for i in name_tuple: print(i) #支援切片,方式跟list一樣 name_tuple[1:2] #應用在list上面的方法如果不會更改元組內容都可以使用 list(name_tuple) #將元組資料轉換成串列 max(name_tuple) #獲得元組內容最大值 min(name_tuple) #獲得元組內容最小值 #enumerate物件使用在元組 tuple(enumerate_name) ``` ### 字典 * 也是串列型的資料,但是利用 鍵-值 的方式配對儲存 * 取用時也是用鍵key取值value * 個別字典可以放近串列內,串列也可以放進字典內,字典內也可以放字典 ``` python # v = {key:value, key:valu,....} score={'小民':90, '大徐': 80} #或是 score={ '小民': 90, '大徐': 80, } score = {} #建立空字典 score['Keven'] = 95 #新增一筆字典到變數裏 print(score['大徐']) #印出大徐的數字 len(score) #字典累元素的數量 if keyname in score: #檢查keyname(key值)是否在字典裡 for i,j in score.items(): #將字典內的鍵-值列一遍 #如果用for i,j in score: ,會將鍵拆成字,列出每個鍵的前兩個字 score.keys() #資料所有的key #範例 for i in score.keys(): #也可以用 for i in score : score.values() #資料所有的value del score[鍵] #刪除某個特定鍵元素 del score #刪除整個字典 score.clear() #刪除字典內所有元素,但字典還在,是個空串列 n_score = score.copy() #將字典完整複製到n_score內 sorted(score.key()) #字典排序,依據鍵 score.fromkeys(list_name) #使用串列list_name建立字典,串列內容會變成字典的鍵,如果沒有設定value值,則用none當值 score.fromkeys(list_name, 'abc') #同上,但是設定每個值都是abc score.get('abc') #搜尋字典的鍵,如果鍵存在則傳回該鍵的值,如果不存在則傳回預設值 #如果沒有設定預設值則傳回none #設定預設值的方式(預設值是defaultvalue) score.get('abc', 'defaultvalue') score.setdefault('abc') score.setdefault('abc', 'defaultvalue') #方法和get()相同,不同的地方是如果沒有這個鍵值,就會新增此元素 #沒有設定預設值則傳回none,有設定則依照設定\ score.pop(key[, default]) #依照key搜尋,找到就將該元素刪除,同時回傳刪除元素的值,如果沒有找到就傳回default的設定內容,如果沒有設定default則傳回keyError訊息 #建議還是設定default比較好 ----------------------------------------- 範例 #將字典放進串列內,創出50個字典元素,並更改前3個字典的值 sold = [] for i in range(50): solder={'tag':'red', 'speed':'slow', 'order':i} sold.append(solder) for solder in sold[:3]: solder['tag'] = 'green' print(sold[:5]) ----------------------------------------- ``` ### 集合 set * 無序,每個元素都是唯一的(有重複時會刪除掉多的,只剩一個) * 集合本身是可變的(可以增減元素),但是元素的內容是不可變的 * 整數、浮點數、字串、元組可當成元素,但是串列、字典、集合等不可以當成元素(會產生錯誤訊號),如果使用set()來建立集合,則可以放串列、字典、集合等元素,但是會將這些元素轉換成集合型元素 * 類別名稱是set ``` python langs = {1,'abc',('g','h','i')} langs = set() #建立集合的方式,用第二種可以建立空集合 集合的操作 & 交集 intersection() c = A & B c = A.intersection(B) | 聯集 union() c = A | B c = A.union(B) - 差集 difference() c = A - B c = A.difference(B) ^ 對稱差集 symmetric_difference() c = A ^ B c = A.symmertic_difference(B) == 等於 != 不等於 in 是成員 not in 不是成員 A.add(元素) #增加一個元素到集合內 A.clear() #刪除集合所有元素,傳回值是none A.copy() #淺拷貝方式Shallow copy複製集合(改變一個集合內容時,另一個不會被改變) A.difference_update(B[,C,....]) # 刪除A集合內與B集合(或B,C....集合)重複的元素,傳回值是None A.intersection_update(B[,C,....]) # A集合改成B集合(或B,C....集合)的交集(沒交集的元素就刪除),傳回值是None A.isdisjoint(B) # 如果兩個集合沒有共同的元素則傳回True,否則傳回False A.issubset(B) # A集合是否是B集合的子集合(B集合的元素是否在A集合全部都有),傳回值是布林值 A.isupperset() A.pop() # 隨機刪除元素,所刪除的元素將被傳回,如果是空集合則傳回錯誤 A.discare() # 刪除特定元素,如果沒有這個元素,也不會出現錯誤訊息 A.remove(元素) # 刪除特定元素,如果沒有這個元素,會出現錯誤訊息 A.symmetric_difference_update(B) # 刪除A集合內有A,B兩集合內都有的元素,並將B集合內沒有出現在A集合的元素加到A集合,和symmetric_difference()結果相同,只是被改變的集合不同 A.update(B[,C,....]) # 將B集合(或B,C....集合)的元素加到A集合中,傳回值是None max(A) #集合內的最大值,如果是字串,則列出unicode碼的最大值 min(A) #集合內的最小值,如果是字串,則列出unicode碼的最小值 sum(A) #集合內數值總和,不可用在字元或字串元素 len(A) # 列出集合元素數量 sorted(A) # 排序集合元素,並將結果存在新的串列物件內,原來的集合不會更動 enumerated(A) # 傳回連續整數配對的enumerated物件 frozenset({'a','b','c'}) #凍結集合,凍結後就不可以再變了,不可以使用add或remove等指令,但可以執行 & | ^ 等集合查詢 ----------------------------------------- 範例 langs = set('DeepBlue Mouton') print(langs) #結果顯示 {'D', ' ', 'p', 'e', 'M', 't', 'u', 'l', 'o', 'n', 'B'} #字串被拆成字母,且重複的會刪除 li = ['apple', 'banana', 'orange', 'apple'] langs = set(li) #也可以寫成 langs = set(['apple', 'banana', 'orange', 'apple']) print(langs) #結果顯示{'banana', 'apple', 'orange'},會刪除重複的 集合操作範例 A = {'a', 'b', 'c'} B = {'c', 'd', 'e'} A & B # {'c'} A | B # {'a', 'b', 'd', 'c', 'e'} A - B # {'a', 'b'} B - A # {'d', 'e'} A ^ B # {'d', 'e', 'a', 'b'} A == B # False A != B # True 'd' in A # False 'd' not in A # True ----------------------------------------- ``` ### Hash 雜湊 * 一個物件在其生命週期內,如果保持不變,就是hashable * 官方說法:如果一個物件在其生命週期內,其雜湊值Hash Value從未改變(這需要一個__hash__()方法),並且可以與其他物件進行比較(這需要一個__eq__()或__cmp__()方法),那麼這個物件就是可雜湊的。雜湊物件的相等意味著其雜湊值的相等。 * 雜湊性使得物件可以用作dictionary鍵和set成員,因為這些資料結構在內部使用了雜湊值。 * Python的所有不可變的內建物件都是可hashable的,但可變容器(如列表或字典)並非如此。對於使用者定義的類的例項,預設情況下是可雜湊的;它們都是不相等的,並且它們的雜湊值都是id()。 * list、set和dictionary 都是可改變的,比如list.append(),set.remove(),dict['key'] = value,是不可雜湊的; * tuple和string是不可變的,只可以做複製或者切片等操作,這就是可雜湊的。 ## 常用函式 ``` python # sum():算總和 # len():算長度 li = [1, 2, 3, 5] alcount = sun(li) allength = len(li) help(print) #列出print函數的說明 dir(__builtins__) #列出所有內建函數 dir(物件名稱) #列出適用本物件的內建函數 map(function, iterable) #会根据提供的函数或指令對串列做映射 #參數1需使用處理用的函數,後面可以放多個迭代(如串列) ----------------------------------------- 範例 sq = list(map(lambda x: x ** 2, [1, 2, 3, 4, 5])) print(sq) #輸出[1, 4, 9, 16, 25] sq = list(map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])) print(sq) #輸出[3, 7, 11, 15, 19] map(str,li) #列如將串列內的所有元素都改成文字 ----------------------------------------- filter(function, iterable) #過濾序列用 #參數1需使用判斷用的函數,參數2則需要可迭代對象,如串列 #返回值是一個列表,格式是filter ----------------------------------------- 範例 def is_odd(n): return n % 2 == 1 #此時回傳True或False newlist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) for i in newlist: print(i) #逐一印出奇數 ----------------------------------------- print() #https://www.programiz.com/python-programming/methods/built-in/print print("aa",var1,"bb",var2) #將字串連再一起,,會在字串間產生一個空格 print("aa"+var1+"bb"+var2) #將字串連再一起,,字串間無空格 print('%d %.2f %s' % (1, 99.3, 'Justin')) #字串格式化輸出 print("輸出格式區"%(變數系列區,'''''') #格式化輸出 %d 整數 %f浮點數 %x 16進位 %o 8進位 %s 字串 #%(+|-)m.nf 浮點數,m保留多少格數供輸出(含小數點),n保留多少格數空間 #%(+|-)nd %(+|-)no %(+|-)nx %(+|-)ns n是保留多少格數空間 #保留空間不足將完整輸出資料,如果保留個數空間太多,資料靠右對齊 #https://openhome.cc/Gossip/Python/StringFormat.html print('%d %.2f %s' % (1, 99.3, 'Justin'),end = " ") #跟上面一樣,但是結尾會增加end指定的字串,如果end寫成end='',就變成結尾不換行 print("{}資料內容{}資料內容{}以下推".format(var1,var2,var3)) #搭配format的用法 print("write to docs this word", file=fileObj) #將文字列印到檔案 id() #查看變數的位址 input() #輸入資料,回傳一率是字串類型,要計算需要轉換成數字 name = input("請輸入姓名:") in not in #判斷一個物件是否位於另一個物件內,物件可以是字串、串列、元組、字典等 # a in b ,回傳布林值 is is not #判斷兩個物件是否相同,物件可以是字串、串列、元組、字典等 #並不只是內容相同,而是指物件變數指向相同記憶體 pass #跳過目前步驟,不作任何處理,也沒有任何輸出 replace("=", ">") #取代,把 = 換成 > apply() #對DataFrame裡面的每一條Series都做同一動作,動作寫在()裏 print("abc","GGG") #在螢幕上印出結果:abcGGG zip #可以將幾個可迭代物件(如串列)對應打包為一個新的元組 #如果參數串列的長度不相等,會以最少的數量為準做匹配, ----------------------------- 範例: li=["a","b","c"] li2 = ["AAA","BBB","CCC"] zipdata = zip(li, li2) for i,j in zipdata: print(i,j) #逐一輸出tuple的對應內容 print(zipdata) #輸出tuple格式 print(list(zipdata)) #輸出串列格式 i,j = zip(*zip) #將zip拆解開來,指定給i跟j兩條序列 ``` --- ## 自訂函式 function ``` python def 名稱(參數1, 參數2 [,.....]): 程式內容 z = x + y return z #呼叫方式 def 名稱(參數1,參數2="sa"): #將參數設預設值,有預設值的參數2在呼叫時不一定要寫,另外呼叫時也可以指定新的值給參數2 #參數也可以放串列,函式內也可以修訂串列內容,修訂後原串列也會跟著改變 #若是使用關鍵字行參數,呼叫時格式為 參數名稱=值,如 名稱(參數1='A',參數2='B') def multivar(*參數): #可以任意輸入1到多個參數 #如果普通參數和任意數量參數並存,必須將任意參數放在()的最右邊 #呼叫方式 multivar('AAA') multivar('aaa','bbb','ccc') #傳進去的參數格式是tuple元組 def multivar(**參數): #使用任意數量的關鍵自行參數,可以自行輸入關鍵字 #此時參數格式為dict字典 #呼叫 multivar(city='AAA',lang='EN',locat='AMR') ----------------------------- 範例: def a(var): for i in var: print(i) mb = ["a","b","c"] a(mb) def a(var,g): if g == 1: var.append("dfdf") return var mb = ["a","b","c"] a(mb,1) #函數修改串列後,原串列會跟著改變 #採用以下寫法,原串列不會被改變 a(mb[:],1) ------------------------------ def fname(1, 3) def fname(參數2="a",參數1="b") #此方式可以讓參數的位置亂跳,但參數名稱需配合def後面的參數名稱 #有預設值得參數放後面,這樣呼叫時比較不會有順位的錯誤 def fname(參數1,參數2): return 參數1-參數2 #回傳參數設計,回傳參數可以不只一個,用,分開 #如果沒有設定回傳值(return),則參數會預設傳回None #回傳值可以是數值、字串、字典等 ----------------------------- 範例: def fname(參數1,參數2): return 參數1+參數2,參數1-參數2,參數1*參數2,參數1/參數2 #呼叫 add, sub, mul, div =fname(20,10) ------------------------------ 遞迴式函數設計 #每次呼叫自己時,會讓使用範圍越來越小 #必須有一個終止條件 ----------------------------- 範例: def fact(n): if n == 1: return 1 else: return (n*fact(n-1)) #產生階乘運算(例如fact(5) 回傳1*2*3*4*5的值) ------------------------------ lambda #稱為匿名函數,不需要函數名稱的函數 ----------------------------- 範例: vsqu = lambda s:s.replace('g', '') #範例是輸入s,輸出s.replace('g', '') #呼叫 print(vsqu("abcdefgh")) ------------------------------ ``` * 區域變數:只有在該函數內使用,跳出函數外不能使用(該函數記憶體空間在函數執行完成後就刪除了) * 全域變數:一般在主程式就建立,函數內也可以引用 * 如果區域變數和全域變數名稱相同,在函數內會使用區域變數的的值,在函數外則使用全域變數的值 --- ## 函式庫 Package ``` python #import 函式庫名稱 import datetime datetime.datetime.now() import ipywidgets as widgets #ipysidgets是Jupyter上開發的可互動的函式庫,可以有拉桿、按鈕、點選等多種互動功能 # https://ipywidgets.readthedocs.io/en/latest/ ``` --- ## 流程控制 ``` python if (條件) : 程式 elif (條件) : 程式 else: 程式 list = [1,2,3,4] for i in list #和下列方法相同 for i in [1,2,3,4] print(i) for i in range(0,1000) print(i) #印出0到999,從0開始到end-1,所以是999 for i in range(0,1000,2) ##印出0到999,從0開始到end-1,間隔是2 for i,j in zip([1,2,3], [4,5,6]): print(i,j) #zip 可以一次用兩條序列來跑for迴圈,也可以三條...... break #強制跳出for迴圈 continue #符合條件就不執行本次運算,繼續執行下一次 #for i in range(20): # if i < 10:continue #輸出的值是超過10的才會輸出 else #一旦for的條件都不能滿足時,就執行else的部分 #if i in range(3): # print(i) #else: # print("over") --------------------------------------------- 範例: squares = [] nn = list(range(n+1)) for i in nn: squares.append(i ** 2) 可以濃縮成 square = [num ** 2 for num in range(1,n+1)] 尋找質數的範例 n = int(input("輸入數字")) if n == 2: print("%d是質數" % n) else: for i in range(2,n): if n % i == 0: print("%d不是質數" % n) break else: print("%d是質數" % n) ---------------------------------------------- i = 0 while i < 8: print(i) i = i + 1 #印出0到7,主要是重複執行,並判斷條件是否成立(True),成立就繼續,不成立(False)就跳出 #如果設計變成無限迴圈,按Ctrl+C可以中斷 break #強制離開迴圈 continue #符合條件就不執行本次運算,繼續執行下一次 範例 while True: #這種寫法會一直不斷重複,可搭配break做跳出 li = ["a","b","c","d"] while li: #重複迴圈,直到li變成空串列 li.pop() #移除最後一個元素 ``` --- ## 運算子 * 關係運算子 > >= < <= == != * 邏輯運算子 and or not --- ## 類別 (物件導向) * 類別名稱第一個字母必須大寫 * 類別中包含屬性(attribute)和方法(method) * 類別中的方式是時就是自訂函數,但只有屬於該類別的物件可以調用,其他地方不能用 * ``` python -------------------------------------------------- 範例: #基礎類別宣告與調用 class cy(): #cy後面的()也可以省略 title='OO國小' def sname(n): return n #調用 print(cy.title) print(cy.sname("AAA")) -------------------------------------------------- #初始化類別的method,指宣告這個類別物件時,將自動執行這個方法,又稱建構元constructor __init__() -------------------------------------------------- 範例: class cy(): def __init__(self,uname,money): self.name = uname self.balance = money self.title="taipei Bank" #調用 bcla = cy("白先生",100) print(bcla.title) #呼叫類別時,會自動執行init方法,參數「白先生」會傳給init的uname,100會傳給money #init的self是必須的,也必須放在所有參數最前面,初始化類別時會自動傳入這個參數,代表的是類別本身的物件 #未來在類別內想要參照各屬性與函數執行運算皆使用self -------------------------------------------------- ``` * 屬性 * 類別內的屬性可以讓外部引用的稱為共有屬性,可以讓外部引用的方法稱為公有方法,公有屬性可以在類別外修改 * 私有:無法在類別外修改私有類別屬性,也無法在類別外呼叫的私有方法 ```python= -------------------------------------------------- 範例: class cy(): def __init__(self,uname,money): self.__name = uname self.__balance = money self.__title="taipei Bank" print(self.__title) #私有屬性 def __rcate(self,ud): print(ud) #私有方法,僅可以在類別內部被呼叫 def tod(self,rd): self.__rcate(rd) #呼叫私有方法__rcate,並傳入rd參數 #調用 bcla = cy("白先生",100) bcla.tod(123) #類別內的屬性宣告時加上__即可變成,外部就無法修改了 -------------------------------------------------- ``` * 繼承 * 被繼承的類別稱為父類別(parent class)或基底類別(base class) * 繼承的類別稱為子類別(child class)或衍生類別(derived class) * 優點是許多父類別的公有屬性或方法,在子類別中可以直接引用,另外子類別也可以有自己的屬性與方法 * 基底類別必須在衍生類別前面 * 衍生類別要取得基底類別的私有屬性,只能在基底類別使用return的方式將私有屬性包在方法內被呼叫 * 衍生類別也可以有自己的__init__方法,如果衍生和基底類別的方法或屬性名稱重複,會先使用衍生類別的,不行時再使用基底類別的 ```python= -------------------------------------------------- 範例: class cy(): def __init__(self,uname,money): self.__name = uname self.__balance = money self.__title="taipei Bank" #私有屬性 class shi(cy): #繼承cy類別的屬性及方法 pass #調用 bcla = shi("白先生",100) print(bcla.title) -------------------------------------------------- ``` * 衍生類別使用基底類別方法 * super() ```python= -------------------------------------------------- 範例: class A(): def __init__(self, aname, aage): self.name = aname.title() self.age = aage print(self.name, self.age) class B(A): #繼承基底類別A def __init__(self,bname,bage): super().__init__(bname,bage) #使用A列別的init方法,變數不用加self. #直接將變數傳給A類別的init方法處理 #加上super()可以繼承A類別的所有屬性及方法 三層的使用: class A(): def __init__(self): self.Aname = "AAA" def get_info1(self): print('A class is', self.Aname) class B(A): #繼承A類別 def __init__(self): self.Bname = "BBB" super().__init__() # 將A類別的init複製,B類別就有A類別的屬性 def get_info2(self): print('B class is', self.Bname) class C(B): #繼承B類別 def __init__(self): self.Cname = "CCC" super().__init__() # 將B類別的init複製,C類別就有A跟B類別的屬性,若是方法method就不需要用這個,直接繼承 def get_info3(self): print('C class is', self.Cname) tt = C() tt.get_info1() 旁枝的使用 class A(): def __init__(self): self.Aname = "AAA" class BA(A): #繼承A類別 def __init__(self): self.Bname = "BBB" super().__init__() # 將A類別的init複製 class CA(A): #繼承A類別 def __init__(self): self.Cname = "CCC" super().__init__() # 將A類別的init複製 def get_info(self): print("A name is",self.Aname, "\n B name is",BA().Bname, #取得旁枝BA類別的屬性,記得加上() "\n C name is",self.Cname) tt = CA() tt.get_info() 多重繼承 class A(): def __init__(self): self.name = "FA" def info1(self): self.Aname = "Aname" print("A") class BA(A): #繼承A類別 def info2(self): self.BAname = "BAname" print("BA") class CA(A): #繼承A類別 def __init__(self): super().__init__() # 將A類別的init複製 def info3(self): self.CAname = "CAname" print("CA") class DBC(BA,CA): #同時繼承BA跟CA類別 def __init__(self): super().__init__() # 將CA類別的init複製,同時可擁有A類別的init屬性 self.DBCname = "DBCname" def info4(self): print("DBC") t = DBC() print(t.DBCname) t.info1() print(t.name) -------------------------------------------------- isinstance(物件, 類別) #查詢物件是否屬於該類別,是的話返回True #有繼承時,子類別的物件屬於父類別時(True),但父類別的物件並不一定屬於子類別 -------------------------------------------------- 依上面範例: isinstance(t,DBC) #返回True isinstance(t,A) #返回True,有繼承關係 Y = CA() isinstance(Y,DBC) #返回False,繼承關係是從上到下,下屬于上,上不屬於下 -------------------------------------------------- ``` --- ## 特殊方法 ``` python __doc__ #列出文檔內的 ''' ''' 標示的註解 __name__ #判斷程式是自己執行或是被import當成模組使用 #自己執行時,__name__ = __main__ #當成模組使用時,輸出模組名稱 __str__() #可以協助返回較容易讀取的字串 -------------------------------------------------- 範例: class name: def __init__(self,bn): self.name = bn def __str__(self): #定義返回容易閱讀的字串 return self.name t = name("Hang") print(t) #有__str__返回 Hang #沒有__str__返回 <__main__.name object at 0x10e5340d0> -------------------------------------------------- __repr__() #和__str__方法相同,因為單純在python Shell,系統會呼叫shell做回應 #可以在class內寫上 __repr__ = __str__ __iter__() #將類別設計成迭代物件(類似list),提供for使用,會搭配next()及raise Stoplteration -------------------------------------------------- 範例: class Fib(): def __init__(self,max): self.max = max def __iter__(self): self.a = 0 self.b = 1 return self def __next__(self): fib = self.a if fib > self.max: raise StopIteration self.a, self.b = self.b, self.a + self.b return fib for i in Fib(100): print(i) -------------------------------------------------- ``` --- ## 模組 * 模組安裝:在python shell內輸入 pip install 模組名稱 * 模組更新:pip install -U 模組名稱 * mac安裝:sudo pip3 install 模組名稱 * 將類別或函數寫好後,另存成 模組名稱.py 的檔案即可 * 呼叫時使用: import 模組名稱 使用時要用 模組名稱.函數名稱 或是 模組名稱.類別名稱 * 也可以僅呼叫模組內的個別函數或個別類: from 模組名稱 import 函數名稱 from 模組名稱 import 類別名稱 (未來程式內引用時可以不寫模組名稱) * 導入多個函數或類別: from 模組名稱 import 函數名稱1,函數名稱2,函數名稱3...... from 模組名稱 import 類別名稱1,類別名稱2,類別名稱3...... * 導入模組內所有函數或類別 from 模組名稱 import * 此方法和import 模組名稱 大略相同,不同的是未來程式內引用時可以不寫模組名稱 * 替代名稱 as import 模組名稱 as 替代名稱 from 模組名稱 import 函數名稱 as 替代名稱 * 如果匯入的模組超過1個,而且這些模組之間有繼承的關係,那都要import才行 --- ## 檔案處理 * 使用os或os.path模組 ```python os.getcwd() #取得現在的所在目錄 os.path.abspath(檔名) #取的檔案的絕對路徑 -------------------------------------------------- 範例: print(os.path.abspath('.')) #目前所在資料夾的絕對路徑 print(os.path.abspath('..')) #目前所在位置的上一層資料夾絕對路徑 print(os.path.abspath('Untitled1.ipynb')) #該檔案的絕對路徑 -------------------------------------------------- os.path.relpath(path, start) #傳回從start(檔案或位置)到path的相對路徑清單 #若start省略,則傳會工作目錄到path間的相對路徑清單 #anaconda並不會正確顯示 os.path.basename(path) #去除目錄的路徑,回傳檔案名稱(包含附檔名) os.path.exists(path) #path的檔案或資料夾如果存在傳回True,否則傳回Fase os.path.isabs(path) #path的檔案或資料夾是絕對路徑傳回True,否則傳回Fase(mac根目錄前要加上/,例如/Users/ycp/Desktop/質因數.ipynb) os.path.isdir(path) #path是資料夾傳回True,否則傳回Fase,(path要用絕對路徑) os.path.isfile(path) #path是檔案傳回True,否則傳回Fase os.mkdir(path) #建立path的目錄,建議搭配os.path.exists檢查目錄是否已存在 os.rmdir(path) #刪除path目錄,限制只能是空的目錄 os.remove(path) #刪除path檔案 os.chdir(path) #切換目前工作資料夾到path os.path.join(參數1, 參數2, 參數3,......) #將參數內的字串結合成一個檔案路徑 os.path.getsize(檔名) #取得檔案的大小,單位是位元組 os.listdir() #以串列方式列出該目錄的內容 os.walk(path) #列出該path之下所有的檔案和資料夾(目錄數) #返回迭代物件,每一個包含3個值:工作目錄名稱(字串),該工作目錄下所有的子目錄(串列),,該工作目錄下所有的檔案(串列) -------------------------------------------------- 範例: import os for dirname, subd, fname in os.walk('finlab_course/'): #for 後面的順序不可以亂,但名稱可以自取 print("工作目錄名稱:",dirname) print("子目錄名稱串列",subd) print("檔案串列",fname) -------------------------------------------------- ``` * 關於檔案的常用函數(不用引入os) ```python open(path, mode="r") open(path, "w") open(path, "w", encoding="utf-8") #開啟一個檔案,mode有不同開啟選項,不寫就是r #如果檔案不存在,又是w模式,則open會建立該檔案,若檔案存在,則會清空該檔案 #r讀取,w覆蓋式寫入,a附加式寫入,x開啟一個新的檔案提供寫入(如果檔案已經存在會產生錯誤) #b開啟按進為檔案模式,t開啟文字檔案模式,+開啟檔案供更新用 #微軟系統的ANSI編碼為cp950(encoding="950") #encoding = "utf-8-sig" 採用LE的BOM編碼系統 # http://mt116.blogspot.com/2017/08/coding-system-unicode.html read() #讀取檔案內容 close() #關閉檔案 with open(path) as 檔案物件 : #此方法也可檔案物件,但是可以不必關閉檔案,系統會在不需要此檔案時自動將檔案關閉 readlines() #逐行讀取檔案內容,以串列方式儲存,每行的換行字元皆會儲存在串列內 find(字串) #搜尋字串是否有在目標字串內,回傳找到的索引位置(從0開始) write(輸出的資料) #輸出資料寫入到檔案內,無法寫入數值資料,要寫入資料請將數值轉字串 #輸出多行資料,請逐行寫write,如果要斷行請在每行最後加上\n ----------------------------- 範例: file_obj = open('test.html') data = file_obj.read() file_obj.close() print(data) #可以改寫如下 with open('test.html') as file_obj: data = file_obj.read() print(data) #逐行讀取檔案內容 with open('test.html') as file_obj: for i in file_obj: print(i) #另一種寫法 objlist = file_obj.readlines() #readlines之前不可以有read,不然讀不出來 print(objlist) #搜尋數據 a = "abcd" b = "c" if b in a: print("ok") #或是使用 index = a.find(b) if index >= 0: print("ok") #數值寫入檔案的方法 with open('test.html') as file_obj: file_obj.write(str(100)+'\n') file_obj.write("ABC") ----------------------------- ``` * 使用glob模組 ```python import glob glob.glob #獲得特定資料夾的的內容,返回串列 #可以使用萬用字元* -------------------------------------------------- 範例: glob.glob('/Users/ycp/Desktop/*') glob.glob('*.txt') glob.glob('Untitled*.ipynb') -------------------------------------------------- ``` * 使用shutil模組 * 檔案或目錄的複製、刪除、更動位置、更改名稱 ```python import shutil shutil.copy(source, destination) #檔案複製,複製時source檔一定要存在 shutil.copytree(source, destination) #目錄複製,複製時source目錄一定要存在,複製時連同裡面的資料也會複製 shutil.move(source, destination) #檔案移動,移動時source檔一定要存在,移動後,source檔案不再存在,source跟destination的名稱不一定要相同 #也可以用來改檔名或目錄名 #也可以移動目錄,移動時連同該目錄下的子目錄也會一並移動 #移動目錄時也可以同時改名 shutil.rmtree(目錄名稱) #刪除底下有資料的目錄,刪除就是刪除,不會在垃圾桶 -------------------------------------------------- 範例: import shutil shutil.move("t2.txt", "./gg") #移動t2.txt到同位置的gg子目錄內,寫成.//gg亦可 shutil.move("t2.txt", "t3.txt") #更改檔案名稱 -------------------------------------------------- ``` * 使用send2trash模組 ```python import send2trash send2trash.send2trash(目錄名稱) #刪除底下有資料的目錄,刪除後會在垃圾桶 ``` * 使用zipFile模組 * 檔案或目錄壓縮或解壓縮 ```python import zipfile fz = zipfile.ZipFile("dn.zip", "w") #先建立物件,fz是物件名稱,dn.zip是未來輸出的zip檔 # w 代表未來是供write方法寫入 # zipfile()無法執行整個目錄直接壓縮,因此採用迴圈方式逐一寫入zip檔 namelist() #傳回zip檔案內所有被壓縮的檔案或目錄名稱,以串列方式傳回此物件 infolist() #搭配namelist(),傳回個元素的屬性,檔案名稱filename,檔案大小file_size,壓縮結果大小compress_size,檔案時間date_time extractall() #解壓縮zip檔 -------------------------------------------------- 範例: import zipfile,glob,os with zipfile.ZipFile('archive.zip', 'w') as zf: for i in glob.glob("/Users/ycp/Desktop/t*"):#逐一處理gg資料夾的所有檔案 zf.write(i, os.path.basename(i), zipfile.ZIP_DEFLATED) # zipfile.ZIP_DEFLATED 是壓縮方式 zf.close #關閉物件 #讀取壓縮檔案內檔案的各項屬性 import zipfile listfile = zipfile.ZipFile("archive.zip", "r") print(listfile.namelist()) #以串列方式列出檔案名稱 for i in listfile.infolist(): print(i.filename, i.file_size, i.compress_size, i.date_time) #逐一列出各項屬性 #解壓縮zip檔案 listfile = zipfile.ZipFile("archive.zip") listfile.extractall('out') #解壓縮後的資料夾名稱 listfile.close -------------------------------------------------- ``` * 使用pyperclip剪貼簿模組 ```python pip install pyperclip #安裝模組 -------------------------------------------------- 範例: import pyperclip pyperclip.copy("AAA") #將字串拷貝到剪貼簿 string = pyperclip.paste() #將剪貼簿拷貝回string print(string) -------------------------------------------------- ``` --- ## 程式錯誤(異常)處理與LOG紀錄 * 一般城市有錯誤或異常時,或印出錯誤訊息,並停止程式繼續執行,使用異常處理時,有捕捉到異常資訊時,會執行異常處理部分的指令,和後跳過異常的那一行繼續執行下一行 * 錯誤說明: * AttributeError 通常指物件沒有這個屬性 * Exception 一般錯誤皆可使用,可以提供捕捉各種基礎異常用 * FileNotFoundError 找不到檔案 * IOError 在輸入或輸出時發生錯誤 * IndexError 索引超出範圍區間 * KeyError 在映射中沒有這個鍵 * MemoryError 需求記憶體空間超出範圍 * NameError 物件名稱未宣告 * SyntaxError 語法錯誤 * SystemError 直譯器的系統錯誤 * TypeError 資料型別錯誤 * ValueError 傳入無效參數 * ZeroDivisionError 除數不可以為0 ```python def division(x, y): try: return x/y #指令或處理程序 except ZeroDivisionError: # 捕捉到異常 print("除數不可以為0") # 異常時執行此列 #回傳一個None finally: print("階段任務完成") #finally不一定要寫 #有寫時,這個try之下一定會執行finally #在def之下,如果無誤會先回傳finally內容,再傳回try,如果有異常,先執行except,再執行finally,最後返回主程式輸出None print(division(10,2)) # 參數無誤,執行return x/y print(division(5,0)) # 除數為0,執行異常處理程序 print(division(6,2)) #上一行錯誤跳過後繼續處理這一行 #將上面的改寫成: #增加程式的可讀性 def division(x, y): try: ans = x/y #先處理此程序 except ZeroDivisionError: # 捕捉到異常 print("除數不可以為0") # 異常時執行此列 else: return ans #如果無誤執行此程序 #分析檔案開啟錯誤 fn="test.txt" try: with open(fn) as file_obj: data = file_obj.read() except FileNotFoundError: print("找不到%s檔案"%fn) except TypeError: #可以設定不只一個錯誤捕捉 print("資料型別錯誤") except (FileNotFoundError, TypeError): #一個except同時捕捉多的錯誤 print("有錯誤") else: wordnum = data.spilt() print("字數:", len(wordnum)) #使用內建的錯誤訊息 def division(x, y): try: return x/y except ZeroDivisionError as e: # 使用python內建異常或錯誤訊息 print(e) #捕捉所有異常 def division(x, y): try: return x/y except: print("有異常") #會回傳一個None ``` * 自行定義抹些狀況為異常,丟出異常訊息,程式停止往下執行,同時讓程式跳到自行設計的except去執行 ```python raise exception("自訂的錯誤訊息") ...... try: 指令 except Exception as err: #err是任意取名的變數,內容就是raise exception後面的訊息 指令 #範例,搭配traceback模組將錯誤資訊記錄在檔案中 import traceback def passwd(pwd): pwdlen = len(pwd) if pwdlen < 5: raise Exception("密碼長度不足") if pwdlen > 8: raise Exception("密碼長度太長") print("密碼長度正確") try: passwd("aabbccdd") except Exception as err: errlog = open("errlog.txt","a") errlog.write(traceback.format_exc()) #寫入錯誤資訊內容 errlog.close() print(err) assert 條件, '字串' #程式檢查點 #程式執行至此,需滿足條件(True),才可以繼續執行 #如果執行無法滿足條件(False),程式會泡出異常,並印出後面的字串 for i in range(100): assert i<=20 ,'i大於20' print(i) #讓系統停止assert運作 #找到phthon.exe(windows下)存放的資料夾,在cmd模式下,切換到該資料夾,並輸入 # python.xe -O D:\pdata\ch12.py # -O 後面是指要執行中斷assert的程式路徑 ``` * 程式LOG * logging模組5個等級(上到下。由小到大) * DEBUG:顯示最低層級的內容 * INFO:紀錄一般發生的事件 * WARNING:顯示的內容不會影響程式執行,但未來可能導致問題發生 * ERROR:程式在某些狀態引發錯誤的原因 * CRITIAL:通常顯示將會讓整個系統當掉或中斷的錯誤 ```python import logging #使用下列函數設定顯示資訊的等級,為來只有此等級或更高等級的LOG會被顯示 lgooing.basicConfig(level=logging.DEBUG) #DEBUG等級 #指令 logging.debug('字串') logging.info('字串') logging.warning('字串') logging.error('字串') logging.critical('字串') #印出的結果會有前導訊息,如 DEFUB:root: lgooing.basicConfig(level=logging.DEBUG, format=' ') #加上 format=' ' 可以取消前導訊息 lgooing.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s : %(message)s') #訊息前面加上紀錄時間及顯示層級 #anaconda中,若level有修改,要restart kernel才會正確顯示 logging.basicConfig(filename='logout.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s : %(message)s') #filename可以將log存在檔案內,但此時螢幕就不會出現log訊息 #重複執行時,後面的log會在檔案接下去寫入 #須參考層級,如果低於設定的level,也不會存入檔案內 範例: import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s : %(message)s') logging.debug('程式開始') for i in range(5): logging.debug('目前索引 %s' %i) logging.debug('程式結束') logging.disable(level) #停用log,level是指層級 logging.disable(logging.CRITIAL) ``` ## 正規表達式 Regular Expression * 使用 re 模組 * https://docs.python.org/2/library/re.html * 規則說明: * \d 表示0-9數字字元 * \d+ 表示不限長度的數字(使用+,其他規則類推) * \D 除了0-9之間的整數字元以外的其他字元 * \s 空白、定位、Tab鍵、換行、換頁字元 * \S 除了空白、定位、Tab鍵、換行、換頁字元之外的其他字元 * \w 數字、字母和底線_字元[A-Za-z0-9_] * \W 除了數字、字母和底線_字元[A-Za-z0-9_]之外的其他字元 * | 或者 'mary|john' 表示mary或john皆符合,字串周邊不可以有空格 * (字串?) ()內的可有可無,皆算符合 (\d?) * (字串*) ()內的字串出現0到多次皆符合 (\w*) * (字串+) ()內的字串出現1到多次皆符合,和*不同的是至少要有1次,否則回傳None (\s+) * \d{4} 4個數字,{}內是重複次數 * 字元分類形式,[ ]內不需使用\ * [a-z] 搜尋a-z的小寫字元 * [A-Z] 搜尋A-Z的大寫字元 * [aeiouAEIOU] 搜尋aeiouAEIOU這10個字元 * [2-5.] 搜尋2-5的數字和 . (在[]內不用寫成\.) * [^ ] []加上^,搜尋不在[]內規則的所有字元 * '^字串' ''前面加上^,表示搜尋的字串必須出現在被搜尋字串的起始位置才算成功 * '字串$' ''後面加上$,表示搜尋的字串必須出現在被搜尋字串的最後位置才算成功 * '^字串$' 前後都加,被搜尋的句子一定只有一個字串 * '.字串' .是萬用字元,代表除了換行字元之外的所有字元,但是只有一個字元(搜尋符號.要使用\.) * '.' 搜尋所有字元,換行字元除外 * re.DOTALL 可以搜尋包括換行字元,放在search(),findall(),compiler()的Flags位置 * re.VERBOSE 可以在pattern規則內分行時加上註解 * re.I 或是 re.IGNORECASE 忽略字母大小寫 ```python= import re word = r'\d\d\d\d-\d\d\d-\d\d\d' #定義規則 ckrule = re.compile(word) #建立規則物件 # ckrule = re.compile(r'\d\d\d\d-\d\d\d-\d\d\d') msg1 = "我的電話是0932-321-123" msg2 = "我有兩隻電話:0955-234-543,0977-778-543,你保留一下" for i in range(1,3): pnum = ckrule.search(locals()['msg%s'%i]) #使用search搜尋字串中符合的規則的 # locals()['msg%s'%i]是動態設定變數名稱msg1,msg2 #search只會找到第一組相符的,第二組以後的不會找 #回傳格式 <re.Match object; span=(5, 17), match='0932-321-123'> #沒有相符的傳回None print(pnum) pnum = ckrule.findall(locals()['msg%s'%i]) #search也可以改用findall,會找全部相符的 #回傳格式串列 ['0955-234-543', '0977-778-543'],沒有相符的傳回None #上面範例也可以改寫如下 import re word = r'\d{4}-\d{3}-\d{3}' #規則可以改寫成這樣的,相同的用{}填入數字 msg1 = "我的電話是0932-321-123" msg2 = "我有兩隻電話:0955-234-543,0977-778-543,你保留一下" for i in range(1,4): pnum = re.findall(word, locals()['msg%s'%i]) #減少compile,直接使用re #格式是re.findall(規則, 字串, Flags),Flags可以省略 #格式是re.search(規則, 字串, Flags),Flags可以省略 print(pnum) #搜尋結果轉成tuple import re word = r'(\(\d{2}\))(\d{8})' msg1 = "我的電話是(02)12345678" msg2 = "我有兩隻電話:(03)12345678,(03)87654321,你保留一下" for i in range(1,3): pnum = re.search(word, locals()['msg%s'%i]) pnumf, pnumb = pnum.groups() #pnum.groups()的格式是tuple #可以多項指定,也可以使用pnum.groups(0) 類推 print(pnumf,pnumb) #search可以搭配group()或group(n)取出相符的子字串,例如規則是'John(son|pon|agr)' word='john(son|per|iqe)(aa|bb)' msg = "I see johnsonaa ,johnperbb and johnppdcc go to out room" mtw = re.search(word, msg) print(mtw) print(mtw.group()) print(mtw.group(1)) #son print(mtw.group(2)) #aa #使用?查詢,範例import re word = r'(\(\d{2}\))?(\d{8})' msg1 = "我的電話是(02)12345678" msg2 = "我有兩隻電話:12345678,(03)87654321,你保留一下" for i in range(1,3): pnum = re.findall(word, locals()['msg%s'%i]) print(pnum) #使用?查詢 word = 'john((gg)*son)' msg = "I call johnggggggson" pnum = re.search(word, msg) print(pnum.group()) #搜尋忽略字母大小寫 word = 'john|TOM' msg = "John, tom and Mary" pnum = re.findall(word, msg, re.I) #Flag位置寫re.I 或是 re.IGNORECASE 皆可 print(pnum) #重複次數範例 (son){3} #搜尋'sonsonson',所以'sonsons'不符合 (son){2,4} #搜尋'sonson','sonsonson','sonsonsonson'皆符合 (son){2,} #son重複2次以上皆符合(含2次) (son){,10} #son重複10次以下皆符合(含10次) 範例 pattern = '(son){3,5}' #貪婪模式:python預設模式,以下範例執行會找出'sonsonsonsonson'(最多符合的) pattern = '(son){3,5}?' #非貪婪模式:以下範例執行會找出'sonsonson'(最少符合的) msg = 'sonsonsonsonson' txt = re.search(pattern, msg) print(txt.group()) MatchObject物件 re.match #和re.search功能相同,不同的是re.match搜尋字串最前面,找不到就回傳None #使用match和search搜尋成功時可用 txt = re.match(pattern, msg) txt.group() #傳回搜尋到字串 txt.end() #傳回搜尋到字串的結束位置 txt.start() #傳回搜尋到字串的起始位置 txt.span() #傳回搜尋到字串的(起始,結束)位置 result = re.sub(pattern, newstr, msg) #pattern取代規則 #newstr要更換的新內容 #msg搜尋的字串 #搜尋成功時,將結果字串傳給result,原msg不受影響 #搜尋到多筆相同字串時,這些字串全部被取代 #搜尋失敗,將msg內容傳給result 範例 import re pattern = r'CIA (\w)\w*' newstr = r'\1***' #\1表示第一組對映(\w)使用原字,剩下的\w*全用*** msg = 'CIA Mark , CIA Mary, CIA John' txt = re.sub(pattern, newstr, msg) print(txt) #印出 M*** , M***, J*** #Flag同時放許多參數 dstr = re.search(pattern, msg, re.VERBOSE|re.IGNORECASE|re.DOTALL) 比較複雜的正規是表達範例 #電話號碼處理 pattern = r'''( (\d{2}|\(\d{2}\))? #區域號碼,可省略 (\s|-)? #分隔符號(-或是空格),可省略 \d{8} #電話號碼 (\s*(ext|ext.)\s*\d{2,4})? #分機號碼2-4位數號碼 )''' #電子郵件搜尋 pattern = r'''( [a-zA-Z0-9_.]+ #帳號 @ # @ 符號 [a-zA-Z0-9_.]+ #domain \. # . 符號 [a-zA-Z]{2,4} #機構分類com edu等 ([\.])? # . 符號,可省略 ([a-zA-Z]{2,4})? #國碼,美國是省略的 )''' ``` ###### tags: `PYTHON`