一、IO編程
IO在計算機中指的是Input/Output,凡是用到數據交換的地方都會涉及IO編程,例如磁盤、網絡數據傳輸。
1.文件讀寫
1.打開文件
python內置了讀寫文件的函數,文件讀寫之前需要打開文件,確定文件的讀寫模式,默認是讀模式,默認緩衝區是無。
函數原型:open(name [.mode [.buffering]])
name:文件路徑
mode:操作文件的模式,默認是讀模式
buffering:緩衝區,默認緩衝區是無
例:
file = open(r'c:\dome.txt') #以讀模式打開一個文件
2.文件模式
mode參數有很多選項,可以根據需要設置:
注:特別留意‘b’參數的使用,一般處理文本文件時,是用不到‘b’參數的,但是處理其他類型的文件(二進制文件),比如MP3或圖像,那麼應該在參數中增加‘b’,在爬蟲中處理媒體文件很常用。
3.緩衝區
open函數第三個可選參數buffering控制着文件的緩衝:
參數爲0:直接將數據寫到磁盤上
參數爲1:I/O操作就是有緩衝的,數據線寫到內存,只有調用flush函數和close函數纔會將數據更新到磁盤
參數爲大於1:表示緩衝區的大小,單位是字節
參數爲-1或是任何負數:表示使用默認的緩衝區大小
4.文件讀取
文件的讀取主要分爲按字節讀取和按行讀取,常用到的方法有read()、readlines()和close()。
read():一次將文件內容讀取到內存,如果文件過大,會出現內存不足問題。此時,可以使用read(size)反覆調用讀取的方式實現。
readline():每次讀取一行內容,此方法用於讀取配置文件等文本文件比較合理。
readlines():一次讀取所有內容並按行返回列表。
例:
1.簡單實現
file = open(r'c:\demo.txt')
data = file.read() #將文件demo.txt的字符一次性讀取出來
file.close() #關閉io操作,否則會佔用系統資源
print(data)
2.由於讀取io操作可能會出現異常,一旦出現異常,後面close就得不到執行,爲了保證程序的健壯性,上面的代碼還可以用try…finally實現。
try:
file = open(r'c:\demo.txt')
data = file.read() #將文件demo.txt的字符一次性讀取出來
print(data)
finally:
if file:
file.close() #關閉io操作,否則會佔用系統資源
3.一種更簡單的方法,with語句
with open(r'c:\demo.txt') as fileReader:
print(fileReader.read())
5. 文件寫入
讀文件和寫文件一樣,唯一區別在於調用open()方法時,傳入的模式是‘w’或者‘wb’。
try:
file = open(r'c:\demo.txt','w')
file.write('hello word')
finally:
file.close()
#調用write()函數時,操作系統不是立即將數據寫到文件中,二十先寫入內存,等到空閒的時候在寫入文件中,最後調用close()方法就是將數據完整的寫入文件中,當然也可以用flush()方法。
2. 操作文件和目錄
python對文件和目錄的操作經常用到os模塊和shutil模塊,常用到的方法主要有:
1.獲取當前python腳本的工作目錄路徑:
os.getcwd()
2.返回指定目錄下的所有文件和目錄名:
os.listdir(path)
3.刪除一個文件:
os.remove(filepath)
4.驗證給出的路徑是否是一個文件:
os.path.isfile(filepath)
5.驗證給出的路徑是否是一個目錄:
os.path.isdir(filepath)
6.判斷是否是絕對路徑:
os.path.isabs(filepath)
7.驗證路徑是否存在:
os.path.exists(filepath)
8.分離一個路徑的目錄名和文件名:
os.path.split(),例:os.path.exists(r'c:\demo.txt'),返回一個元組:('c;\','demo.txt')
9.分離擴展名:
os.path.splitext()。例如:os.path.splitext(r'c:\demo.txt'),返回一個元組:('c:\demo','.txt')
10.獲取路徑名:
os.path.dirname(filepath)
11.獲取文件名:
os.path.basename(filepath)
12.讀取和設置環境變量:
os.getenv()與os.putenv()
13.給出當前平臺使用的終止符:
os.lineesp.Windows使用'\r\n',linue/unix使用的是‘\r'
14.指示你正在使用的平臺:
os.name,windows是’nt',linux/unix用戶是‘posix'
15.重命名文件或目錄:
os.rename(old,new)
16.創建多級目錄:
os.makedirs(r'c:\test\test')
17.創建單個目錄:
os.mkidr('test')
18.獲取文件屬性:
os.stat(file)
19.修改文件權限與時間戳:
os.chmod(file)
20.獲取文件大小:
os.path.getsize(filename)
21.複製文件夾:
shutil.copytree('olddir','newdir'),olddir和newdir都必須是目錄,且newdir必須不存在
22.複製文件:
shutil.copyfile('oldfile','newfile'),oldfile和newfile都只能是文件
23.複製文件:
shutil.copy('oldfile','newfile'),oldfile只能是文件,newfile可以是文件,也可以是目錄
24.移動文件(目錄):
shutil.move('oldpos','newpos')
25.刪除目錄:
os.rmdir('dir'),只能刪除空目錄;shutil.rmtree('dir'),刪除任意目錄
二、進程和線程
1.多進程
python實現多進程的方式有兩種,一種是使用os模塊中的fork方法,二是使用multiprocessing模塊。這兩種方法的區別在於前者僅適用於linux/unix,不支持windows平臺,後者則是跨平臺的實現。
1.使用fork創建多進程
fork方法來自於linux/unix系統中提供的fork系統調用,fork一次調用返回兩次,子進程永遠返回0,父進程返回子進程的進程id。
import os
if __name__ = '__main__':
print('current process %s start...'%(os.getid())
pid = os.fork()
if pid < 0:
print('error in fork')
elif pid == 0 :
print('I am child process %s and my parent process is %s'%(os.getpid(),os.getppid())
else:
print('I %s create a child process %s'%(os.getpid(),pid))
2.使用multiprocessing模塊創建多進程
import os
from multiprocessing import Process
#進程執行的代碼
def run_proc(name):
print('I am child %s (%s) running'% (name,os.getpid()))
if __name__ == '__main__':
print('Parent process %s' %(os.getpid()))
for i in range(5):
pro = Process(target=run_proc,args=(i,))#傳遞進程需要執行的函數名和參數name
pro.start()
print('Process %s will start' % (i))
pro.join()
print('end.')
3.pool進程池
multiprocessing模塊提供了一個Pool類來代表進程池對象,Pool提供指定數量的進程供用戶調用,默認是CPU的核數。當有新的請求提交到pool時,如果進程池沒有滿,那麼就會創建新的進程來執行該請求,若進程池已經達到最大值了,那麼該請求就會等待,直到池中有進程結束,纔會分配進程池中的進程來執行該請求。
import os,time,random
from multiprocessing import Pool
def run_task(name):
print('Task %s pid = %s is runing' %(name,os.getpid()))
time.sleep(random.random() * 3)
print('Task %s is end'%(os.getpid()))
if __name__ == '__main__':
print('Current process is %s'%(os.getpid()))
pool = Pool(processes=3) #創建容量爲3的進程池
for i in range(5):
pool.apply_async(run_task,args=(i,))
print('Waiting for all subprocess done')
pool.close() #調用close()方法後就不在繼續添加新進程了
pool.join() #等待所有子進程執行完畢,但調用join()前,必須先調用close()
print('All subprocess done')
運行結果:
Current process is 5160
Waiting for all subprocess done
Task 0 pid = 3040 is runing
Task 1 pid = 7060 is runing
Task 2 pid = 268 is runing
Task 3040 is end
Task 3 pid = 3040 is runing
Task 7060 is end
Task 4 pid = 7060 is runing
Task 268 is end
Task 3040 is end
Task 7060 is end
All subprocess done
從結果中的pid就可以看出(任務0和任務3pid相同),當添加到5個任務,但是一開始只運行了3個,且最多能運行3個,直到有任務結束,進程池分配進程給新任務,任務才能執行
4.進程間通信
python提供了多種進程間通信的方式,如Queue,Pipe、Value+Array等,可自行查看用法。
Queue:用來在多進程間進行通信
Pipe:常用來在兩個進程間進行通信
2.多線程
python標準庫提供了兩個模塊:thread和threading,thread是低級模塊,threading是高級模塊,對thread進行了封裝,絕大多數情況下,我們只需要使用threading這個高級模塊。
1. 用threading模塊創建多線程
(1)方式1,傳入函數並創建Thread實例
import threading
#線程執行函數
def run_thread(args):
print('Current %s is runing...'%(threading.current_thread().name))
print('agrs[] = %s'%(args))
print('%s end.'%(threading.current_thread().name))
if __name__ == '__main__':
print('Current %s is running...'%(threading.current_thread().name))
t1 = threading.Thread(target=run_thread,name='thread1',args=('args1',))
t2 = threading.Thread(target=run_thread, name='thread12', args=('args2',))
t1.start()
t2.start()
t1.join()
t2.join()
print('Current %s is end...' % (threading.current_thread().name))
(2)方式2,繼承threading.Thread類並創建線程類,然後實現__init__和run方法
import threading
class MyThread(threading.Thread):
def __init__(self,name,args):
threading.Thread.__init__(self,name = name)#調用父類init方法
self.agrs = args
def run(self):#線程執行是run方法,將操作代碼寫在這裏
print('Current %s is runing...'%(threading.current_thread().name))
print('agrs[] = %s'%(self.agrs)) #拿到剛賦值的參數
print('%s end.'%(threading.current_thread().name))
if __name__ == '__main__':
print('Current %s is running...'%(threading.current_thread().name))
t1 = MyThread(name='thread1',args='args1') #通過構造方法創建線程
t2 = MyThread(name='thread2',args='args2')
t1.start()
t2.start()
t1.join()
t2.join()
print('Current %s is end...' % (threading.current_thread().name))
2.線程同步
使用Thread對象是Lock和RLock實現簡單的線程同步,這兩個對象都有acquire和release方法,對於那些只允許一個線程操作的數據可以將其操作放入acquire和release方法之間。
對於Lock對象而言,如果一個線程連續兩次調用acquire操作,那麼由於一次調用之後沒有release,第二次調用acquire將掛起線程,這會導致Lock對象永遠不會release,使線程死鎖。
RLock對象允許一個線程多次對其進行acquire操作,因爲在其內部通過一個counter變量維護着線程acquire的次數,而且每次acquire操作都必須有一個release操作與之對應,在所有的release操作完成後,別的線程才能申請該RLock對象。
import threading
myLock = threading.Rlock()
num = 0
....
myLock.acquire()
對num操作
myLock.release()
....
3.協程
協程又稱微線程,纖程,是一種用戶級的輕量線程。協程擁有自己的上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候恢復先前保存的寄存器上下文和棧。因此協程能保留上一次調用時候的狀態。協程需要用戶自己來編寫調度邏輯,對於CPU來說,協程其實是一個單線程,所以CPU不用去考慮怎麼調用、切換上下文,這就省去了CPU的切換開銷,所以協程一定程度上又好於多線程。
python通過yield提供了對協程的基本支持,單是不完全,而使用第三方gevent庫是最好的選擇,個vent提供了比較完善的協程支持。gevent是一個基於協程的python網絡函數庫,使用greenlet在libev事件循環頂部提供了一個高級別併發性的API。
4.分佈式進程
分佈式進程指將process進程分佈到多臺機器上,充分利用多臺機器的性能處理複雜任務,multiprocessing模塊不到支持多進程,其中的manages子模塊還支持把多進程分佈到多臺機器上。
例如我們做爬蟲程序,我們想抓取某個網站的所有圖片,如果使用多進程,則一般用一個進程負責抓取圖片鏈接,另一個進程負責通過圖片鏈接進程下載和存儲到本地,這種情況可以寫成分佈式。
例:windows版
服務進程端:
#coding:utf-8
from multiprocessing.managers import BaseManager
from multiprocessing import freeze_support
from queue import Queue
#第一步,創建task_queue和result_queue,用來存放任務和結果
task_number = 10
task_queue = Queue(task_number)
result_queue = Queue(task_number)
def get_task():
return task_queue
def get_result():
return result_queue
#定義manager類
class QueueManger(BaseManager):
pass
def win_run():
#第二步:利用register方法把創建的隊列註冊到網絡,callback關聯隊列
QueueManger.register('get_task_queue',callable = get_task)
QueueManger.register('get_result_queue',callable = get_result)
#第三步:綁定地址和端口,設置口令
queueManger = QueueManger(address=('127.0.0.1',8001),authkey='key'.encode('utf-8'))
try:
# 第四步:啓動管理,監聽信息管道
queueManger.start()
print('queueManger start...')
#第五步:通過管理實例的方法,獲取網絡訪問的queue對象
task = queueManger.get_task_queue()
result = queueManger.get_result_queue()
#第六步:添加任務
for url in ['ImageUrl_' + str(i) for i in range(10)]:
print('put task %s'%(url))
task.put(url)
print('try get result...')
for i in range(10):
print('get result %s'%(result.get(timeout=10)))
except:
print('manager error')
finally:
#關閉管道
queueManger.shutdown()
if __name__ == '__main__':
freeze_support()
win_run()
任務進程端:
#coding:utf-8
import time
from multiprocessing.managers import BaseManager
class QueueManager(BaseManager):
pass
#第一步:使用QueueManager註冊用於獲取queue的方法名稱
QueueManager.register('get_task_queue')
QueueManager.register('get_result_queue')
#第二步:連接到服務器
server_addr = '127.0.0.1'
print('Connect to server %s ...'%(server_addr))
#端口和驗證口令
manager = QueueManager(address=(server_addr,8001),authkey='key'.encode('utf-8'))
manager.connect()
#第三步:獲取queue的對象
task = manager.get_task_queue()
result = manager.get_result_queue()
#第四步:從task隊列獲取任務,並把結果寫到result中
while(not task.empty()):
image_url = task.get(True,timeout=5)
print('run task download %s ...'%(image_url))
time.sleep(1)
result.put('%s--->success'%(image_url))
print('work exit.')
三、網絡編程
1.Socket類型
套接字格式爲:socket(family,type[,protocal]),使用給定的地址族、套接字類型、協議編號來創建套接字。
2.Socket函數
常用的python網絡編程函數如下: