一、异常处理
什么是异常?异常是程序运行时发生错误的信号(程序发生错误时,不处理,就会抛出异常)
错误分为 1.语法错误2.逻辑错误
异常包括三部分:异常追踪信息、异常类、异常值。
异常处理方法:
1.如果错误发生的条件是可预知的,我们需要用if来处理,在错误发生之前来预防.
1 age = 10
2 while True:
3 a = input("<<<").strip()
4 if a.isdigit():
5 a = int(a)
6 if a== age:
7 print("you got it ")
8 break
2.如果错误发生的条件是不可预知的,用try...expect,在错误发生之后进行处理.
1 t = (i for i in range(5)) 2 while True: 3 try: 4 print(t.__next__()) 5 except StopIteration: 6 print("finish") 7 break
1 t = (i for i in range(5)) 2 while True: 3 try: 4 print(t.__next__()) 5 except StopIteration: 6 print("finish") 7 break 8 else: 9 print("没有异常") #当try执行时,else就会执行 10 finally: 11 print("无论异常与否,都会打印")
二、网络编程
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序,而程序的pid是同一台机器上不同进程或者线程的标识.
套接字ipc:一台主机上多个应用程序之间的通讯。套接字有两种,一种基于文件类型套接字:家族名:AF_UNIX 另一种基于网络类型套接字:家族名:AF_INET
客户端与服务端的交互过程:从服务端说起:创建一个socket对象,然后与端口进行绑定bind,对端口进行监听listen,调用accept阻塞直到客户端进行连接,客户端初始化一个socket对象,调用connect与服务端进行连接,向服务端发送请求,客户端接受并处理请求,然后把回应数据,客户端获得数据,最后关闭连接,一次交互就结束。
1 #实例化socket对象 2 import socket 3 socket.socket(socket_family,socket_type,protocal=0) 4 socket_family :套接字家族,AF_UNIX(基于文件)AF_INET(基于网络) 5 socket_type:SOCK_STREAM(流),SOCK_DGRAM 6 7 获取tcp/ip套接字 8 tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 9 获取udp/ip套接字 10 udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 11 from socket import * 12 tcp_socket = socket(AF_UNIX,SOCK_STREAM) 13 udp_socket = socket(AF_INET,SOCK_DGRAM)
s= socket(socket.AF_INET,socket.SOCK_STREAM)
服务端套接字函数
s.blid() 绑定(主机,端口号)到套接字
s.listen() 开始tcp监听
s.accept() 阻塞,直到客户端进行连接
客户端套接字函数
s.connect() tcp服务端连接
#公共用途套接字函数
s.recv()接受tcp数据
s.send()发送tcp数据
s.recvfrom()接受udp数据
s.sendto()发送udp数据
s.close()关闭套接字
1 #tcp客户端 2 import socket 3 tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 tcp_server.bind(("127.0.0.1",8000)) 5 tcp_server.listen(5) 6 conn,addr = tcp_server.accept() 7 print(conn,addr) 8 data = conn.recv(1024) 9 print("接受客户端的信息",data.decode("utf-8")) #接受信息 10 conn.send(data.upper()) 11 conn.close() 12 tcp_server.close() 13 # tcp服务端 14 import socket 15 tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 16 tcp_client.connect(("127.0.0.1",8000)) 17 tcp_client.send(b"hello") #发送信息 18 data2 = tcp_client.recv(1024) 19 print("接受服务端的信息:",data2.decode("utf-8")) 20 tcp_client.close()
1 #udp服务端 2 import socket 3 udp_server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 4 udp_server.bind(("127.0.0.1",8000)) 5 msg,addr = udp_server.recvfrom(2014) 6 print("接受客户端的信息",msg.decode("utf-8")) 7 udp_server.sendto(msg.upper(),addr) 8 import socket 9 udp_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 10 udp_client.sendto(b"hello",("127.0.0.1",8000)) 11 msg,addr = udp_client.recvfrom(1024) 12 print("接受服务端的信息",msg.decode("utf-8"))
基于tcp的套接字
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接
1 #tcp服务端 2 1 ss = socket() #创建服务器套接字 3 2 ss.bind() #把地址绑定到套接字 4 3 ss.listen() #监听链接 5 4 inf_loop: #服务器无限循环 6 5 cs = ss.accept() #接受客户端链接 7 6 comm_loop: #通讯循环 8 7 cs.recv()/cs.send() #对话(接收与发送) 9 8 cs.close() #关闭客户端套接字 10 9 ss.close() #关闭服务器套接字(可选)
1 #tcp客户端 2 1 cs = socket() # 创建客户套接字 3 2 cs.connect() # 尝试连接服务器 4 3 comm_loop: # 通讯循环 5 4 cs.send()/cs.recv() # 对话(发送/接收) 6 5 cs.close() # 关闭客户套接字
udp是无链接的,先启哪一端都可以
1 #udp服务端 2 1 ss = socket() #创建一个服务器的套接字 3 2 ss.bind() #绑定服务器套接字 4 3 inf_loop: #服务器无限循环 5 4 cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送) 6 5 ss.close() # 关闭服务器套接字
1 cs = socket() # 创建客户套接字 2 comm_loop: # 通讯循环 3 cs.sendto()/cs.recvfrom() # 对话(发送/接收) 4 cs.close() # 关闭客户套接字
1 #加入一条socket配置,重用ip和端口 2 3 phone=socket(AF_INET,SOCK_STREAM) 4 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 5 phone.bind(('127.0.0.1',8080))
三、进程
操作系统:一个用来协调、管理计算机硬件和软件资源的系统程序
多道程序设计:提高cpu利用率
进程:一个程序在一个数据集上的一次动态执行过程,负责执行任务的是cpu。
进程包含三部分:程序、数据集、进程控制块。
进程是最小的资源单位
并发:是指系统具有处理多个任务的能力,而一个cpu在同一时刻只能处理一个任务,单个cpu+多道程序设计实现并发
并行:是指系统在同一时刻具有处理多个任务的能力,只有具备多个cpu才能实现
同步:一个进程执行任务的时候,一直等到任务完成,而进程一直处于激活状态
异步:一个进程执行一个任务的时候,不会等到任务完成才执行,当任务完成后,再回来处理。
阻塞调用是指调用结果返回之前,当前线程会被挂起(如遇到io操作)。函数只有在得到结果之后才会将阻塞的线程激活
多进程:由于GIL存在,python中的多线程并不是真正的多线程,如果要充分使用cpu资源,大部分使用的是多进程.
multiprocessing模块
多进程应该避免共享资源。在多线程中,我们可以比较容易地共享资源,比如使用全局变量或者传递参数。在多进程情况下,由于每个进程有自己独立的内存空间,以上方法并不合适。此时我们可以通过共享内存和Manager的方法来共享资源。但这样做提高了程序的复杂度,并因为同步的需要而降低了程序的效率。
Process类
创建的是子进程
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍
1 group参数未使用,值始终为None 2 3 target表示调用对象,即子进程要执行的任务 4 5 args表示调用对象的位置参数元组,args=(1,2,'egon',) 6 7 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18} 8 9 name为子进程的名称
方法介绍:
p.start() ,启动进程,调用子进程中run方法
p.run()进程启动调用的方法,自定义类中必须实现该方法
p.terminate()强制终止进程
p.is_alive() 判断进程是否执行
p.join() 主进程等待p终止,主进程等待,子进程处于运行状态,或达到运行timeout。
属性介绍:
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置.
p.name:进程的名字
p.pid:进程号
p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
注意:在windows中Process()必须放到# if __name__ == '__main__':下
创建并开启子进程的两种方式:
1 import time 2 import random 3 from multiprocessing import Process 4 def print_name(name): 5 time.sleep(random.randint(1,3)) 6 print("my name is %s"%name) 7 8 p1 = Process(target=print_name,args=("Ezhizen",)) #必须加, 9 p2 = Process(target=print_name,kwargs={"name":"Ryoma"}) 10 11 p1.start() #启动进程p1 12 p2.start() #启动进程p2 13 p1.join() #等待p1运行完成 14 p2.join() #等待p2运行完成 15 print("welcome") 16
1 import multiprocessing 2 class MyProcess(multiprocessing.Process): #继承Process类 3 def __init__(self,name): 4 super(MyProcess,self).__init__() 5 self.name = name 6 def run(self): 7 time.sleep(random.randint(1,3)) 8 print("my name is %s"%self.name) 9 10 11 12 p1= MyProcess("Ezhizen") 13 p2 = MyProcess("Ryoma") 14 p1.start() 15 p2.start() 16 p1.join() 17 p2.join() 18 print(" Echizen Ryoma")
1 from multiprocessing import Process 2 from threading import Thread 3 import time 4 def foo(): 5 print(123) 6 time.sleep(1) 7 print("end123") 8 9 def bar(): 10 print(456) 11 time.sleep(3) 12 print("end456") 13 14 15 p1=Process(target=foo) 16 p2=Process(target=bar) 17 18 p1.daemon=True 19 p1.start() 20 p2.start() 21 # time.sleep(1) 22 print("main-------")#打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止
进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,
而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理.
#加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
虽然可以用文件共享数据实现进程间通信,但问题是:
1.效率低(共享数据基于文件,而文件是硬盘上的数据)
2.需要自己加锁处理
1 #由并发变成了串行,牺牲了运行效率,但避免了竞争 2 from multiprocessing import Process,Lock 3 import os,time 4 def work(lock): 5 lock.acquire() 6 print('%s is running' %os.getpid()) 7 time.sleep(2) 8 print('%s is done' %os.getpid()) 9 lock.release() 10 if __name__ == '__main__': 11 lock=Lock() 12 for i in range(3): 13 p=Process(target=work,args=(lock,)) 14 p.start()
为了解决这个问题,采用队列和管道的方法。
Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。
maxsize 是队列中最大允许项数
1 q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
2 q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
3
4 q.get_nowait():同q.get(False)
5 q.put_nowait():同q.put(False)
6
7 q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
8 q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
9 q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样
1 import time,random,os 2 def consumer(q): 3 while True: 4 res=q.get() 5 time.sleep(random.randint(1,3)) 6 print('