一、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库是最好的选择,gevent提供了比较完善的协程支持。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网络编程函数如下: