1.内置模块OS:
- 简介:Python内置的与操作系统文件相关的模块,该模块中语句的执行结果通常与操作系统有关,即有些函数的运行效果在Windows操作系统和MacOS系统中不一样。
- 常用函数:
| 函数名称 | 描述说明 |
| getcwd() | 获取当前的工作路径 |
| listdir(path) | 获取path路径下的文件和目录信息,如果没有指定path则获取当前路径下的文件和目录信息 |
| mkdir(path) | 在指定路径下创建目录(文件夹),如果要创建的文件夹存在,程序会报错FileExitsError |
| makedirs(path) | 创建多级目录 |
| rmdir(path) | 删除path下的空目录,如果要删除的目录不存在,程序报错FileNotFoundError |
| removedirs(path) | 删除多级目录 |
| chdir(path) | 把path设置为当前目录 |
| walk(path) | 遍历目录树,结果为元组,包含所有路径名、所有目录列表和文件列表 |
| remove(path) | 删除path指定的文件,如果要删除的目录不存在,程序报错FileNotFoundError |
| rename(old,new) | 将old重命名为new |
| stat(path) | 获取path指定的文件信息 |
| startfile(path) | 启动path指定的文件 |
- os.path模块:
简介:是os模块的子模块,也提供了一些目录或文件的操作函数。
常用函数:
| 函数名称 | 描述说明 |
| abspath(path) | 获取目录或文件的绝对路径 |
| exists(path) | 判断目录或文件在磁盘上是否存在,结果为bool类型,如果目录或文件在磁盘上存在,结果为True,否则为False |
| join(path,name) | 将目录与目录名或文件名进行拼接,相当于字符串的“+”操作 |
| splitext() | 分别获取文件名和后缀名,结果是元组类型(文件名,后缀名) |
| basename(path) | 从path中提取文件名 |
| dirname(path) | 从path中提取路径(不包含文件名) |
| isdir(path) | 判断path是否是有效路径 |
| isfile(path) | 判断file是否是有效文件 |
2.网络编程与通信协议:
- 通信协议:接入网络所要遵守的“规则”,目前全球通用的通信协议即Internet协议。
- 七层协议与四层协议:
- IP协议:IP协议是整个TCP/IP协议族的核心, IP地址就是互联网上计算机的唯一标识。目前的IP地址有两种表示方式,即IPv4和IPv6。在命令行下使用ipconfig命令可以查看本机的IP地址。
- TCP协议:TCP(Transmission Control Protocol)协议即传输控制协议,是建立在IP协议基础之上的。TCP协议负责两台计算机之间建立可靠连接,保证数据包按顺序发送到。它是一种可靠的、一对一的、面向有连接的通信协议。
- TCP/IP协议中的四个层次:
- TCP/IP协议数据发送和数据接收:
- TCP协议的三次握手:
- UDP协议:UDP协议又被称为用户数据包协议(User DatagramProtocol),它是面向无连接的协议,只要知道对方的IP地址和端口,就可以直接发送数据包,由于是面向无连接的,所以无法保证数据一定会到达接收方。
- 端口号:区分计算机中的运行的应用程序的整数。端口号的取值范围是0到65535,一共65536个,其中80这个端口号分配给了HTTP服务,21这个端口号分配给了FTP服务。
- TCP协议与UDP协议的区别:
| TCP协议 | UDP协议 | |
| 连接方面 | 面向连接的 | 面向无连接 |
| 连接方面 | 传输消息可靠、不丢失、按顺序到达 | 无法保证不丢包 |
| 传输效率方面 | 传输效率相对较低 | 传输效率高 |
| 连接对象数量方面 | 只能是点对点、一对一 | 支持一对一、一对多、多 对多的交互通信 |
3.Socket:
- Socket通信模拟图:
- Socket对象的常用方法:
| 方法名称 | 功能描述 |
| bind((ip,port)) | 绑定IP地址和端口 |
| listen(N) | 开始TCP监听,N表示操作系统挂起的最大连接数量,取值范围1-5之间,一般设置为5 |
| accept() | 被动接收TCP客户端连接,阻塞式 |
| connect((ip,port)) | 主动初始化TCP服务器连接 |
| recv(size) | 接收TCP数据,返回值为字符串类型,size表示要接收的最大数据量 |
| send(str) | 发送TCP数据,返回值是要发送的字节数量 |
| sendall(str) | 完整发送TCP数据,将str中的数据发送到连接的套接字,返回之前尝试发送所有数据,如果成功为None,失败抛出异常 |
| recvfrom() | 接收UDP数据,返回值为一个元组(data,address),data表示接收的数据,address表示发送数据的套接字地址 |
| sendto(data,(ip,port)) | 发送UDP数据,返回值是发送的字节数 |
| close() | 关闭套接字 |
4.TCP通信:
- TCP服务器端流程如下:
(1)使用socket类创建一个套接字对象。
(2)使用bind((ip,port))方法绑定IP地址和端口号。
(3)使用listen()方法开始TCP监听。
(4)使用accept()方法等待客户端的连接。
(5)使用recv()/send()方法接收/发送数据6.使用close()关闭套接字。
from socket import socket,AF_INET,SOCK_STREAM
# AF_INET 用于internet之间的进程通信
# SOCK_STREAM表示的是TCP协议编程
# (1)创建socket对象
server_socket=socket(AF_INET,SOCK_STREAM)
# (2)绑定IP地址和端口
ip='127.0.0.1'
port=8888
server_socket.bind((ip,port))
# (3)使用listen()开始监听(5表示最大连接数量)
server_socket.listen(5)
# (4)等待客户端的连接
client_socket,client_addr=server_socket.accept()
# (5)接收来自客户端的数据
data=client_socket.recv(1024)
print("客户端发送过来的数据为:",data.decode("utf-8"))
# (6)关闭socket
server_socket.close()
- TCP客户端的流程如下:
(1)使用socket类创建一个套接字对象。
(2)使用connect((host,port))设置连接的主机IP和主机设置的端口号。
(3)使用recv()/send()方法接收/发送数据。
(4)使用close()关闭套接字。
import socket
# (1)创建socket对象
client_socket = socket.socket()
# (2)IP地址和主机端口,向服务端发送连接请求
ip='127.0.0.1'
port=8888
client_socket.connect((ip,port))
# (3)发送数据
client_socket.send("Welcome to python world".encode('utf-8'))
# (4)关闭
client_socket.close()
- 客户端与服务器端双向通信示意图:
- 服务端与客户端多次通信代码示例:
服务端代码:
import socket
# (1)创建socket对象
socket_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# (2)绑定IP地址和端口号
socket_server.bind(("127.0.0.1",8888))
# (3)设置最大连接数量
socket_server.listen(5)
# (4)等待客户连接
client_sock,client_addr=socket_server.accept()
# (5)接收来自客服端的数据
info=client_sock.recv(1024).decode("utf-8")
while info!="bye":
if info!="":
print("接收到的数据是:", info)
data=input("请输入要发送的数据:") # 服务端要返回的数据
client_sock.send(data.encode("utf-8")) # 向客户端响应数据
if data=="bye":
break
info=client_sock.recv(1024).decode("utf-8")
# (6)关闭socket
socket_server.close()
client_sock.close()
客户端代码:
import socket
# (1)创建socket对象
client_socket = socket.socket()
# (2)主机的IP和端口号
client_socket.connect(('127.0.0.1',8888))
# (3)客户端先发送数据
info=""
while info!="bye":
# 准备要发送的数据
send_data=input("请输入客户端要发送的数据:")
client_socket.send(send_data.encode("utf-8"))
if send_data=="bye":
break
info=client_socket.recv(1024).decode("utf-8")
print("收到服务器响应的数据:",info)
# (4)关闭socket对象
client_socket.close()
5.UDP通信:
- 客户端与服务端通信代码示例:
服务端代码:
from socket import socket,AF_INET,SOCK_DGRAM
# (1)创建socket对象
recv_socket=socket(AF_INET,SOCK_DGRAM)
# (2)绑定IP地址和端口
recv_socket.bind(('127.0.0.1',8888))
# (3)接收来自发送方的数据
recv_data,addr=recv_socket.recvfrom(1024)
print("接收到的数据为:",recv_data.decode("utf-8"))
# (4)准备回复对方的数据
data=input("请输入要回复的数据:")
# (5)回复
recv_socket.sendto(data.encode("utf-8"),addr)
# (6)关闭
recv_socket.close()
客户端代码:
from socket import socket, AF_INET, SOCK_DGRAM
# (1)创建socket对象
send_socket=socket(AF_INET, SOCK_DGRAM)
# (2)准备发送的数据
data=input("请输入要发送的数据:")
# (3)指定接收方的IP地址和端口
ip_port=("127.0.0.1",8888)
# (4)发送数据
send_socket.sendto(data.encode("utf-8"),ip_port)
# 接收数据
recv_data,addr=send_socket.recvfrom(1024)
print("接收到的数据是:",recv_data.decode("utf-8"))
# (5)关闭socket
send_socket.close()
6.程序与进程:
- 程序:英文单词为Program,是指一系列有序指令的集合,使用编程语言所编写,用于实现一定的功能。
- 进程:进程则是指启动后的程序,系统会为进程分配内存空间。
- 创建进程的方式:
方式一 : Process(group=None,target,name,args,kwargs)
参数说明:
(1) group:表示分组,实际上不使用,值默认为None即可。
(2)target:表示子进程要执行的任务,支持函数名。
(3)name:表示子进程的名称。
(4)args:表示调用函数的位置参数,以元组的形式进行传递。
(5)kwargs:表示调用函数的关键字参数,以字典的形式进行传递。
from multiprocessing import Process
import os,time
def test():
print(f"我是子进程,我的PID是:{os.getpid()},我的父进程是:{os.getppid()}")
time.sleep(1)
lst=[]
if __name__ == '__main__':
print("主进程开始执行")
for i in range(5):
p=Process(target=test)
p.start()
lst.append(p)
for item in lst:
item.join() # 阻塞主进程
print("主进程执行结束")
方式二 : class 子进程(Process):
pass
from multiprocessing import Process
import os,time
class SubProcess(Process):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
print(f"子进程名称:{self.name},PID:{os.getpid()},父进程:{os.getppid()}")
if __name__ == '__main__':
print("父进程开始执行")
lst=[]
for i in range(6):
p1=SubProcess(f"进程:{i}")
p1.start()
lst.append(p1)
for item in lst:
item.join()
print("父进程执行结束")
- Process常见的方法和属性:
| 方法/属性名称 | 功能描述 |
| name | 当前进程实例别名 ,默认为Process-N |
| pid | 当前进程对象的PID值 |
| is_alive() | 进程是否执行完,没执行完结果为True,否则为False |
| join(timeout) | 等待结束或等待timeout秒 |
| start() | 启动进程 |
| run() | 如果没有指定target参数,则启动进程后,会调用父类中的 run方法 |
| terminate() | 强制终止进程 |
7.Pool进程池:
- 进程池的原理是:创建一个进程池,并设置进程池中最大的进程数量。假设进程池中最大的进程数为3,现在有10个任务需要执行,那么进程池一次可以执行3个任务,4次即可完成全部任务的执行。
- 创建进程池的语法结构:进程池对象=PooI(N)
- 进程池常用的方法:
| 方法名 | 功能描述 |
| apply _async(func,args,kwargs) | 使用非阻塞方式调用函数func |
| apply(func,args,kwargs) | 使用阻塞方式调用函数func |
| close() | 关闭进程池,不再接收新任务 |
| terminate() | 不管任务是否完成,立即终止 |
| join0 | 阻塞主进程,必须在terminate()或close()之后使用 |
- 非阻塞方式:
from multiprocessing import Pool
import os,time
def task(name):
print(f"子进程PID:{os.getpid()},执行的任务:{name}")
time.sleep(1)
if __name__ == '__main__':
start=time.time()
print("主进程开始执行")
p=Pool(3)
for i in range(10):
p.apply_async(func=task,args=(i,))
p.close() # 关闭主进程不再接收新任务
p.join() # 阻塞父进程,等待所有子任务执行完毕后,才会执行父进程中的代码
print("所有子进程执行完毕,主进程执行结束")
print(time.time()-start) # 5.294420003890991 每次执行三个任务,共执行4次
- 阻塞方式:
from multiprocessing import Pool
import os,time
def task(name):
print(f"子进程PID:{os.getpid()},执行的任务:{name}")
time.sleep(1)
if __name__ == '__main__':
start=time.time()
print("主进程开始执行")
p=Pool(3)
for i in range(10):
p.apply(func=task,args=(i,))
p.close() # 关闭主进程不再接收新任务
p.join() # 阻塞父进程,等待所有子任务执行完毕后,才会执行父进程中的代码
print("所有子进程执行完毕,主进程执行结束")
print(time.time()-start) # 11.40873122215271 每次执行一个任务,共执行10次
- 并发和并行:
并发:是指两个或多个事件同一时间间隔发生,多个任务被交替轮换着执行
并行:是指两个或多个事件在同一时刻发生,多个任务在同一时刻在多个处理器上同时执行
8.队列:
- 队列:进程之间可以通过队列(Queue)进行通信,队列是一种先进先出(FirstIn First Out)的数据结构。
- 创建队列的语法结构:队列对象=Queue(N)
- 队列常用方法:
| 方法名称 | 功能描述 |
| qsize() | 获取当前队列包含的消息数量 |
| empty() | 判断队列是否为空,为空结果为True,否则为False |
| full() | 判断队列是否满了,满结果为True,否则为False |
| get(block=True) | 获取队列中的一条消息,然后从队列中移除,block默认值为True |
| get_nowait() | 相当于get(block=False),消息队列为空时,抛出异常 |
| put(item,block=True) | 将item消息放入队列,block默认为True |
| put_nowait(item) | 相当于put(item,block=False),消息队列为满时,抛出异常 |
- 代码示例:
from multiprocessing import Queue
if __name__ == '__main__':
q=Queue(3) # 创建队列
q.put("hello") # 将hello放入队列
q.put("python") # 将python放入队列
q.put("world") # 将world放入队列
q.get() # 将hello从队列中移除
if not q.empty():
for i in range(q.qsize()):
print(q.get()) # python world
- q.put(item,block=True,timeout=2)
put中的第三个参数单位是秒,表示等待n秒后,队列没有空位置就抛出异常
from multiprocessing import Queue
if __name__ == '__main__':
q = Queue(3)
q.put("hello")
q.put("world")
q.put("python")
q.put("java",block=True,timeout=2) # 等待2秒,队列没有空位置就抛出异常
9.线程:
- 线程:线程是CPU可调度的最小单位,被包含在进程中,是进程中实际的运作单位。一个进程中可以拥有N多个线程并发执行,而每个线程并行执行不同的任务。
- 创建线程:
方式一 : 函数式创建线程:t=Thread(group,target,name,args,kwargs)
参数说明:
(1)group:创建线程对象的进程组
(2)target:创建的线程对象所要执行的目标函数
(3)name:创建线程对象的名称,默认为“Thread-n”
(4)args:用无组以位置参数的形式传入target对应函数的参数
(5)kwargs:用字典以关键字参数的形式传入target对应函数的参数
方式二 : 使用Thread子类创建线程
(1)自定义类继承threading模块下的Thread类
(2)实现run方法
# 函数式创建线程
import threading
from threading import Thread
import time
def test():
for i in range(3):
time.sleep(1)
print(f"线程:{threading.current_thread().name}正在执行{i}")
if __name__ == '__main__':
start = time.time()
print("主线程开始执行")
lst=[Thread(target=test) for i in range(2)]
for item in lst:
item.start()
for item in lst:
item.join()
print(f"一共耗时:{time.time()-start}秒")
# 使用类创建线程
import threading
from threading import Thread
import time
class SubThread(Thread):
def run(self):
for i in range(3):
time.sleep(1)
print(f"线程:{threading.current_thread().name}正在执行{i}")
if __name__ == '__main__':
start = time.time()
print("主线程开始执行")
lst=[SubThread() for i in range(2)]
for item in lst:
item.start()
for item in lst:
item.join()
print("主线程执行完毕")
- 线程操作共享数据的安全性问题:
由于多个线程之间是并发执行,每个线程中的多个任务是并行执行的,就会导致不同线程中的任务会操作同一个全局变量,造成共享数据的安全性问题,因此需要使用Lock对共享数据进行锁定和解锁。
# 售票案例
from threading import Thread,Lock
import threading
import time
lock_obj = Lock()
ticket=50
def sale_ticket():
global ticket
for i in range(100): # 每个排队窗口假设有100人
lock_obj.acquire() # 上锁
if ticket > 0:
print(f"{threading.current_thread().name}正在出售第{ticket}张票")
ticket -= 1
time.sleep(1)
lock_obj.release() # 释放锁
if __name__ == '__main__':
for i in range(3): # 创建三个线程,代表三个窗口
t = Thread(target=sale_ticket)
t.start()
10.生产者与消费者模式
生产者与消费者模式:是线程模型中的经典问题,与编程语言无关。当程序中出现了明确的两类任务,一个任务负责生产数据,一个任务负责处理生产的数据时就可以使用该模式。
- Python内置模块queue由的Queue类:
| 方法名称 | 功能描述 |
| put(item) | 向队列中放置数据,如果队列为满,则阻塞 |
| get() | 从队列中取走数据,如果队列为空,则阻塞 |
| join() | 如果队列不为空,则等待队列变为空 |
| task_done() | 消费者从队列中取走一项数据,当队列变为空时,唤醒调用join()的线程 |