八阁危、第三方庫
主要是
通過pip的方式安裝:
pip3 install PackageName
python3 -m pip install PackageName
sudo pip install PackageName
pip install --upgrade pip #升級pip
pip uninstall flask? #卸載庫
pip list? ? #查看已安裝庫
九朦拖、文件讀寫
讀文件:read
file = open('url.txt','r')
調(diào)用read()方法可以一次讀取文件的全部內(nèi)容狼牺,Python把內(nèi)容讀到內(nèi)存,用一個str對象表示。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? try:? ? f = open('/Users/XXX/Desktop/url.txt','r'encoding='utf-8', errors='ignore')? ? print(f.read()) finally:? ? if f:? ? ? ? f.close()
最后一步是調(diào)用close()方法關(guān)閉文件
但是每次都需要關(guān)閉文件流:(上面的文件可以改寫成)
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? with open('/Users/fuwei/Desktop/url.txt','r', encoding='utf-8', errors='ignore') as file:? print(file.read())
encoding='utf-8', errors='ignore'
中文編碼和忽略非法編碼的字符
寫文件:write
file = open('url.txt','w')
調(diào)用read()方法可以一次讀取文件的全部內(nèi)容月趟,Python把內(nèi)容讀到內(nèi)存杨幼,用一個str對象表示撇簿。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? try:? ? f = open('/Users/XXX/Desktop/url.txt','w'encoding='utf-8', errors='ignore')? ? print(f.read()) finally:? ? if f:? ? ? ? f.close()
最后一步是調(diào)用close()方法關(guān)閉文件
但是每次都需要關(guān)閉文件流:(上面的文件可以改寫成)
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? with open('/Users/fuwei/Desktop/url.txt','w', encoding='utf-8', errors='ignore') as file:? print(file.read())
encoding='utf-8', errors='ignore'
中文編碼和忽略非法編碼的字符
會發(fā)現(xiàn),以'w'模式寫入文件時差购,如果文件已存在四瘫,會直接覆蓋(相當(dāng)于刪掉后新寫入一個文件)。如果我們希望追加到文件末尾怎么辦欲逃?可以傳入'a'以追加(append)模式寫入找蜜。
StringIO和BytesIO[字符流和字節(jié)流]
StringIO和BytesIO是在內(nèi)存中操作str和bytes的方法,使得和讀寫文件具有一致的接口稳析。
操作文件和目錄:
要操作文件洗做、目錄弓叛,可以在命令行下面輸入操作系統(tǒng)提供的各種命令來完成。比如dir诚纸、cp等命令撰筷。
操作文件和目錄
# 查看當(dāng)前目錄的絕對路徑: >>> os.path.abspath('.') '/Users/michael' # 在某個目錄下創(chuàng)建一個新目錄,首先把新目錄的完整路徑表示出來: >>> os.path.join('/Users/michael', 'testdir') '/Users/michael/testdir' # 然后創(chuàng)建一個目錄: >>> os.mkdir('/Users/michael/testdir') # 刪掉一個目錄: >>> os.rmdir('/Users/michael/testdir')
Python的os模塊封裝了操作系統(tǒng)的目錄和文件操作畦徘,要注意這些函數(shù)有的在os模塊中毕籽,有的在os.path模塊中。
十井辆、進程和線程
提高效率:
一種是啟動多個進程影钉,每個進程雖然只有一個線程,但多個進程可以一塊執(zhí)行多個任務(wù)掘剪。
還有一種方法是啟動一個進程平委,在一個進程內(nèi)啟動多個線程,這樣夺谁,多個線程也可以一塊執(zhí)行多個任務(wù)廉赔。
當(dāng)然還有第三種方法,就是啟動多個進程匾鸥,每個進程再啟動多個線程蜡塌,這樣同時執(zhí)行的任務(wù)就更多了,當(dāng)然這種模型更復(fù)雜勿负,實際很少采用馏艾。
總結(jié)一下就是,多任務(wù)的實現(xiàn)有3種方式:
多進程模式奴愉;
多線程模式琅摩;
多進程+多線程模式。
多進程:process
fork(Unix/Linux/Mac)
Unix/Linux操作系統(tǒng)提供了一個fork()系統(tǒng)調(diào)用锭硼,它非常特殊房资。普通的函數(shù)調(diào)用,調(diào)用一次檀头,返回一次轰异,但是fork()調(diào)用一次,返回兩次暑始,因為操作系統(tǒng)自動把當(dāng)前進程(稱為父進程)復(fù)制了一份(稱為子進程)搭独,然后,分別在父進程和子進程內(nèi)返回廊镜。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? import os? def process():? print('Process (%s) start...' % os.getpid())? # Only works on Unix/Linux/Mac:? pid = os.fork() #fork復(fù)制一個子進程? if pid == 0:? ? ? print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))? else:? ? ? print('I (%s) just created a child process (%s).' % (os.getpid(), pid))? if __name__ == '__main__':? process()
由于Windows沒有fork調(diào)用牙肝,fork只能在Unix/Linux/Mac
multiprocessing(多平臺 Unix/Linux/Mac/win)
由于Windows沒有fork調(diào)用,難道在Windows上無法用Python編寫多進程的程序?
由于Python是跨平臺的惊奇,自然也應(yīng)該提供一個跨平臺的多進程支持互躬。multiprocessing模塊就是跨平臺版本的多進程模塊。
multiprocessing模塊提供了一個Process類來代表一個進程對象颂郎,下面的例子演示了啟動一個子進程并等待其結(jié)束:
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? from multiprocessing import Process import os? # 子進程要執(zhí)行方法 def run_proc(name):? ? print('運行子進程 %s (%s)...' % (name, os.getpid()))? if __name__=='__main__':? ? print('父進程 %s.' % os.getpid()) #打印當(dāng)前的進程? ? p = Process(target=run_proc, args=('test',)) #調(diào)用指定方法run_proc 傳入?yún)?shù)? ? print('子進程將運行.')? ? p.start() #啟動子進程? ? p.join()? #將進程添加到調(diào)用的進程? ? print('子進程關(guān)閉')
Pool (進程池的方式批量創(chuàng)建子進程)
如果要啟動大量的子進程吼渡,可以用進程池的方式批量創(chuàng)建子進程:
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? from multiprocessing import Pool import os, time, random? # 子進程要執(zhí)行方法 def run_proc(name):? ? print('運行子進程 %s (%s)...' % (name, os.getpid()))? ? start = time.time()? ? time.sleep(random.random() * 3)? ? end = time.time()? ? print('運行子進程 %s 運行的時間 %2f' % (name,(end - start))) #計算子進程運行的時間? if __name__=='__main__':? print('父進程 %s.' % os.getpid()) #打印當(dāng)前的進程? ? ? p = Pool(4) #創(chuàng)建進程連接池-最大的連接數(shù)是4? for i in range(5):? ? ? p.apply_async(run_proc, args = (i , )) #連接池調(diào)用運行進程的方法,同時傳入?yún)?shù)? ? print('等待全部的進程完成乓序。寺酪。。')? ? p.close() #關(guān)閉連接池? ? p.join()? #調(diào)用全部的子進程執(zhí)行? ? print('全部的進程完成')
添加詳細的每一步的注釋替劈。
代碼解讀:
對Pool對象調(diào)用join()方法會等待所有子進程執(zhí)行完畢寄雀,調(diào)用join()之前必須先調(diào)用close(),調(diào)用close()之后就不能繼續(xù)添加新的Process了陨献。
子進程:
很多時候盒犹,子進程并不是自身,而是一個外部進程眨业。我們創(chuàng)建了子進程后急膀,還需要控制子進程的輸入和輸出。
subprocess模塊可以讓我們非常方便地啟動一個子進程龄捡,然后控制其輸入和輸出卓嫂。
下面的例子演示了如何在Python代碼中運行命令nslookup www.python.org,這和命令行直接運行的效果是一樣的: #!/usr/bin/env python3 # -*- coding: utf-8 -*-? import subprocess? def lookup():? print('$ nslookup www.python.org')? r = subprocess.call(['nslookup', 'www.python.org'])? print('Exit code:', r)? if __name__=='__main__':? lookup()
運行的結(jié)果:
上面的代碼相當(dāng)于nslookup聘殖,利用DNS去解析
如何使用指定DNS服務(wù)器查詢?
語法為 nslookup -qt=類型 目標(biāo)域名 指定的DNS服務(wù)器IP或域名
例子:nslookup -qt=A tool.chinaz.com 8.8.8.8
如果子進程還需要輸入晨雳,則可以通過communicate()方法輸入:
import subprocess? print('$ nslookup') p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = p.communicate(b'set q=mx\npython.org\nexit\n') print(output.decode('utf-8')) print('Exit code:', p.returncode)
進程間通信:
Process之間肯定是需要通信的,操作系統(tǒng)提供了很多機制來實現(xiàn)進程間的通信奸腺。Python的multiprocessing模塊包裝了底層的機制餐禁,提供了Queue、Pipes等多種方式來交換數(shù)據(jù)洋机。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? from multiprocessing import Process, Queue import os, time, random? # 寫數(shù)據(jù)進程執(zhí)行的代碼: def write(q):? print('寫入write進程 : %s ' % os.getpid()) #寫入到隊列的進程? for vlaue in ['A', 'B', 'C']:? print('傳入 %s to 隊列(queue)' % vlaue)? q.put(vlaue) #調(diào)用put方法寫入? time.sleep(random.random())? #去讀數(shù)據(jù)進程執(zhí)行的代碼 def read(q):? print('讀取read進程 : %s ' % os.getpid())? while True:? vlaue = q.get(True)? print('獲取get %s 來自隊列' % vlaue)? if __name__=='__main__':? # 父進程創(chuàng)建Queue坠宴,并且傳給給個子進程:? q = Queue()? pw = Process(target=write, args=(q,))? pr = Process(target=read, args=(q,))? #啟動子進程pw,寫入:? pw.start()? #啟動子進程pr绷旗,讀取:? pr.start()? #等待PW結(jié)束:? pw.join()? #pr讀取進程是死循環(huán)副砍,無法等待其結(jié)束衔肢,職能強行終止:? pr.terminate()
運行結(jié)果:
在Unix/Linux下,multiprocessing模塊封裝了fork()調(diào)用豁翎,使我們不需要關(guān)注fork()的細節(jié)角骤。由于Windows沒有fork調(diào)用,因此,multiprocessing需要“模擬”出fork的效果邦尊,父進程所有Python對象都必須通過pickle序列化再傳到子進程去背桐,所以,如果multiprocessing在Windows下調(diào)用失敗了蝉揍,要先考慮是不是pickle失敗了链峭。
小結(jié)
在Unix/Linux下,可以使用fork()調(diào)用實現(xiàn)多進程又沾。
要實現(xiàn)跨平臺的多進程弊仪,可以使用multiprocessing模塊。
進程間通信是通過Queue杖刷、Pipes等實現(xiàn)的励饵。
多線程:Thread
Python的標(biāo)準(zhǔn)庫提供了兩個模塊:_thread和threading,_thread是低級模塊滑燃,threading是高級模塊役听,對_thread進行了封裝。絕大多數(shù)情況下表窘,我們只需要使用threading這個高級模塊禾嫉。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? import time, threading? #新線程執(zhí)行代碼 def loop():? print('thread is %s runing' % threading.current_thread().name) #打印當(dāng)前主的線程名字? n = 0? while n < 5:? n = n + 1? print('thread %s >> %s' % (threading.current_thread().name,n))#獲取每次的線程? time.sleep(1)? print('thread %s is end ' % threading.current_thread().name)? print('thread %s is runing ..' % threading.current_thread().name) t = threading.Thread(target = loop, name = 'LoopThread') t.start() t.join() print('thread %s ended' % threading.current_thread().name)#打印結(jié)束當(dāng)前主的線程名字
由于任何進程默認(rèn)就會啟動一個線程,我們把該線程稱為主線程蚊丐,主線程又可以啟動新的線程熙参,Python的threading模塊有個current_thread()函數(shù),它永遠返回當(dāng)前線程的實例麦备。主線程實例的名字叫MainThread孽椰,子線程的名字在創(chuàng)建時指定,我們用LoopThread命名子線程凛篙。名字僅僅在打印時用來顯示黍匾,完全沒有其他意義,如果不起名字Python就自動給線程命名為Thread-1呛梆,Thread-2……
Lock
當(dāng)多個線程同時執(zhí)行l(wèi)ock.acquire()時锐涯,只有一個線程能成功地獲取鎖,然后繼續(xù)執(zhí)行代碼填物,其他線程就繼續(xù)等待直到獲得鎖為止纹腌。
balance = 0 lock = threading.Lock()? def run_thread(n):? ? for i in range(100000):? ? ? ? # 先要獲取鎖:? ? ? ? lock.acquire()? ? ? ? try:? ? ? ? ? ? # 放心地改吧:? ? ? ? ? ? change_it(n)? ? ? ? finally:? ? ? ? ? ? # 改完了一定要釋放鎖:? ? ? ? ? ? lock.release()
ThreadLocal:
在多線程環(huán)境下,每個線程都有自己的數(shù)據(jù)滞磺。一個線程使用自己的局部變量比使用全局變量好升薯,因為局部變量只有線程自己能看見,不會影響其他線程击困,而全局變量的修改必須加鎖涎劈。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? import threading? #創(chuàng)建全局的threadloacl對象: local_school = threading.local()? def process_student():? #獲取當(dāng)前現(xiàn)場的關(guān)聯(lián)的student? std = local_school.student? print('hello , %s (in %s )' % (std, threading.current_thread().name))? def process_thread(name):? #綁定threadlocal的student? local_school.student = name? process_student()? t1 = threading.Thread(target = process_thread, args = ('alice', ),name = 'Thread-A') t2 = threading.Thread(target = process_thread, args = ('BOb',),name = 'Thread-B') t1.start() t2.start() t1.join() t2.join()
不同的線程綁定自己線程
全局變量local_school就是一個ThreadLocal對象,每個Thread對它都可以讀寫student屬性,但互不影響蛛枚。你可以把local_school看成全局變量谅海,但每個屬性如local_school.student都是線程的局部變量,可以任意讀寫而互不干擾蹦浦,也不用管理鎖的問題扭吁,ThreadLocal內(nèi)部會處理。
可以理解為全局變量local_school是一個dict白筹,不但可以用local_school.student智末,還可以綁定其他變量,如local_school.teacher等等徒河。
ThreadLocal最常用的地方就是為每個線程綁定一個數(shù)據(jù)庫連接系馆,HTTP請求,用戶身份信息等顽照,這樣一個線程的所有調(diào)用到的處理函數(shù)都可以非常方便地訪問這些資源由蘑。
小結(jié):
一個ThreadLocal變量雖然是全局變量,但每個線程都只能讀寫自己線程的獨立副本代兵,互不干擾尼酿。ThreadLocal解決了參數(shù)在一個線程中各個函數(shù)之間互相傳遞的問題。
正則表達式:
表1.常用的元字符
代碼
說明
.
匹配除換行符以外的任意字符
\w
匹配字母或數(shù)字或下劃線或漢字
\s
匹配任意的空白符
\d
匹配數(shù)字
\b
匹配單詞的開始或結(jié)束
^
匹配字符串的開始
$
匹配字符串的結(jié)束
例子:
\d{3}表示匹配3個數(shù)字植影,例如'010'裳擎;
\s可以匹配一個空格(也包括Tab等空白符),所以\s+表示至少有一個空格思币,例如匹配' '鹿响,' '等;
\d{3,8}表示3-8個數(shù)字谷饿,例如'1234567'惶我。
進階
要做更精確地匹配,可以用[]表示范圍博投,比如:
[0-9a-zA-Z\_]可以匹配一個數(shù)字绸贡、字母或者下劃線;
[0-9a-zA-Z\_]+可以匹配至少由一個數(shù)字毅哗、字母或者下劃線組成的字符串听怕,比如'a100','0_Z'黎做,'Py3000'等等叉跛;
[a-zA-Z\_][0-9a-zA-Z\_]*可以匹配由字母或下劃線開頭,后接任意個由一個數(shù)字蒸殿、字母或者下劃線組成的字符串,也就是Python合法的變量;
[a-zA-Z\_][0-9a-zA-Z\_]{0, 19}更精確地限制了變量的長度是1-20個字符(前面1個字符+后面最多19個字符)宏所。
A|B可以匹配A或B酥艳,所以(P|p)ython可以匹配'Python'或者'python'。
^表示行的開頭爬骤,^\d表示必須以數(shù)字開頭充石。
$表示行的結(jié)束,\d$表示必須以數(shù)字結(jié)束霞玄。
你可能注意到了骤铃,py也可以匹配'python',但是加上^py$就變成了整行匹配坷剧,就只能匹配'py'了惰爬。
re模塊:
Python 里的 re.match() 方法
match()方法判斷是否匹配,如果匹配成功惫企,返回一個Match對象撕瞧,否則返回None。常見的判斷方法就是:
test = '用戶輸入的字符串' if re.match(r'正則表達式', test):? ? print('ok') else:? ? print('failed')
表2.常用的限定符
重復(fù)
代碼/語法
說明
*
重復(fù)零次或更多次
+
重復(fù)一次或更多次
狞尔?
重復(fù)零次或一次
{n}
重復(fù)n次
{n,}
重復(fù)n次或更多次
{n,m}
重復(fù)n到m次
分組:
除了簡單地判斷是否匹配之外丛版,正則表達式還有提取子串的強大功能。用()表示的就是要提取的分組(Group)偏序。比如:
^(\d{3})-(\d{3,8})$分別定義了兩個組页畦,可以直接從匹配的字符串中提取出區(qū)號和本地號碼:
>>> import re >>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345') >>> m.group(0) '010-12345' >>> m.group(1) '010' >>> m.group(2) '12345'