本周首先学习了文件中的高维数据的存储和读取,os模块中的函数以及os.path子模块中的常用函数。认识了网络编程中的网络结构,TCP,IP,IP地址,端口,UDP协议,socket类,TCP/IP协议中的四个层次,TCP/IP协议数据发送和数据接收,TCP协议的三次握手以及Socket对象的常用方法。学习了进程与线程这一模块,知道了进程,线程,程序,进程池,并发,并行,队列等基础知识。

一.文件

1.高维数据的存储与读取

通过将高维数据转为二维数据并将其编码到文件中

import json
lst=[
    {'name':'ysj','age':'20','height':'168'},
    {'name':'lcx','age':'19','height':'170'},
    {'name':'sym','age':'18','height':'165'}
]
#编码
s=json.dumps(lst,ensure_ascii=False,indent=4)#ensure_ascii=False防止中文乱码,indent增加数据的缩进
print(type(s),s)#str类型
#解码
s2=json.loads(s)
print(type(s2),s2)#lst类型
#编码到文件中
with open("test.txt",'w',encoding='utf-8') as f:
    json.dump(lst,f,ensure_ascii=False,indent=4)

2.os模块中的常用函数

从os模块中学习了如何获取当前工作路径os.getcwd(),当前路径下的所有目录及文lst=os.listdir()如何指定路径下所有目录及文件,创建目录os.mkdir(),创建多级目录os.makedirs(),删除目录os.ramdir(),删除多级目录os.removedirs(),改变当前的工作路径os.chdir()改写后再写代码更换工作路径,获取文件信息os.stat(),最近一次访问时间date_format(info.st_atime),该文件创建时间date_format(info.st_ctime),最后一次修改时间date_format(info.st_mtime),文件的大小(单位是字节)date_format(info.st_size),其中date_format函数接受时间戳longtime格式化为可读的时间字符串,info.st_atime调用该函数,启动路径下的文件os.startfile()

import os
print('获取当前工作路径',os.getcwd())
lst=os.listdir()
print('当前路径下的所有目录及文件',lst)#以列表形式呈现
print('指定路径下所有目录及文件',os.listdir('D:\python\Lib\site-packages'))#并不详细
#创建目录
#os.mkdir('好好学习')#如果存在报错
#创建多级目录
#os.makedirs('./aa/bb/cc')
#删除目录
#os.rmdir('好好学习')
#删除多级目录
#os.removedirs('./aa/bb/cc')#一定与创建的多级目录相同
#改变当前工作路径
os.chdir('D:\python\Lib\site-packages\文件')
print('当前的工作路径',os.getcwd())#再写代码,工作路径就是D:\python
#遍历目录树
for dirs,dirlst,filelst in os.walk('D:\python'):
    print(dirs)#文件地址
    print(dirlst)#文件名
    print(filelst)#文件目录
#os模块高级操作
#os.remove('a.txt')#删除文件
#os.rename('./dd.txt','./aa.txt')#重命名,要与路径相匹配
#转换时间格式
import time
def date_format(longtime):
    s=time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(longtime))
    return s
#获取文件信息
info=os.stat('./aa.txt')
print(info)
print('最近一次访问时间:',date_format(info.st_atime)) #date_format函数接受时间戳longtime格式化为可读的时间字符串,info.st_atime调用该函数
#调用前需要进行文件获取信息
print('该文件创建时间:',date_format(info.st_ctime))
print('最后一次修改时间:',date_format(info.st_mtime))
print('文件的大小(单位是字节):',date_format(info.st_size))
#启动路径下的文件
os.startfile('calc.exe')
#启动python解释器
os.startfile('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Python 3.14\Python 3.14 (64-bit).lnk')

3.os.path子模块中常用的函数

获取目录或文件的绝对路径os.path.abspath()

判断目录或文件在磁盘中是否存在os.path.exists()

拼接路径os.path.join()

分隔文件的名和文件的后缀名os.path.spilttext()

提取文件名os.path.basename()

提取路径os.path.dirname()

判断一个路径是否是有效路径os.path.isdir()

判断一个路径是否是有效问件os.path.isfile()

import os.path
print('获取目录或文件的绝对路径:',os.path.abspath('./aa.txt'))#./绝对路径
print('判断目录或文件在磁盘上是否存在:',os.path.exists('./aa.txt'))
print('判断目录或文件在磁盘上是否存在:',os.path.exists('./ad.txt'))
print('拼接路径',os.path.join('D:\python\Lib\site-packages\模块','./aa.txt'))
print('分割文件的名和文件后缀名:',os.path.splitext('b.txt'))#元组类型
print('提取文件名:',os.path.basename(r'D:\python\Lib\site-packages\文件\aa.txt'))#r去掉转义字符
print('提取路径:',os.path.dirname(r'D:\python\Lib\site-packages\文件\aa.txt'))
print('判断一个路径是否是有效路径:',os.path.isdir(r'D:\python\Lib\site-packages'))
print('判断一个路径是否是有效文件:',os.path.isfile(r'D:\python\Lib\site-packages\文件\aa.txt'))

二.网络编程

1.对网络编程中的概念认识

(1)网络结构: 客户端服务器结构网络,是一种主从结构网络。服务器一般处于等待状   态,如果有客户端请求,服务器相应请求,建立连接提供服务。对等结构网络,也被称为点对点网络,每个节点之间是对等的。

(2)TCP(传输控制协议):一种高层次的协议,面向连接的可靠数据传输协议,如果有些数   据包没有收到会重发,并对数据包内容的准确性进行检查并保证数   据包顺序。

(3)IP:是一种低级的路由协议,他将数据拆分在许多小的数据包中,并通过网络将它们发送  到某一特定地址,但无法保证所有包都抵达目的地,也不能保证包的顺序。

(4)IP地址:每台计算机与众不同的标识。分为IPv4和IPv6,在命令行下使用ipconfig查看本  机的IP地址。

127.0.0.1称为回送地址,指本机。主要用于网络软件测试以及本地机进程间的通信,使用回送地址发送数据,不进行任何网络传输,只在本机进程间通信

(5)端口:设备与外界通讯交流的出口,通过端口号确保数据准确送达目标。范围是0~65535。

80-HTTP,21-FTP

(6)UDP协议:又被称为用户数据包协议,面向无连接的协议,只要知道对方的IP地址和端 口,就可以直接发送数据包,由于是面向无连接的,所以无法保证数据一 定会到达接收方。

(7)Socket:网络上的两个程序,通过一个双向链接,实现数据的交换。这个双向链路的一端称为一个socket。通常用来实现客户端和服务器的连接。一个socket有一个IP地址和一个端口号确定,从输入端端口号到输出端端口号一致。

(8)TCP/IP协议中的四个层次

(9)TCP/IP协议数据发送和数据接收

(10)TCP协议的三次握手

2.TCP服务器端代码和客户端代码编写

客户端代码编写                                                        服务器端代码编写

(1)创建socket对象                                           (1)创建socket对象

(2)标明IP地址和主机端口                                (2)绑定IP地址和端口

(3)发送数据                                                     (3)使用listen进行监听

(4)关闭服务器                                                  (4)等待客户端的连接

                                                                             (5)接收来自客户端的数据

                                                                              (6)关闭socket

import socket
#(1)创建socket对象
client_socket=socket.socket()#模块名.类名(),客户端不需要加参数
#(2)标明IP地址和主机端口
ip='127.0.0.1'#服务器的IP和端口
port=8888
client_socket.connect((ip,port))
print('与服务器的连接建立成功')
#(3)发送数据
client_socket.send('weclome to python world'.encode('utf-8'))#发送编码encode
#(4)关闭服务器
client_socket.close()
print('发送完毕')
#先运行服务器后运行客户端
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进行监听
server_socket.listen(5)
print('服务器已经启动')
#(4)等待客户端的连接
client_socket,client_addr=server_socket.accept()#accept接收到的内容,系列解包赋值,分别赋给两个变量
#(5)接收来自客户端的数据
data=client_socket.recv(1024)#接受1024个,从client_socket内容中接收数据并给data
print('客户端发送过来的数据为:',data.decode('utf-8'))#要求客户端发送过来的数据是使用utf-8进行编码的,接收解码decode
#(6)关闭socket
server_socket.close()

3.TCP多次通信服务器和客户端代码编写

在实现多次通信的过程中要用到while循环,以bye作为循环是否进行的循环条件,对接收到的数据进行判断,由客户端发送过来接受并由服务器端向客户端发送client_socket的数据,client_socket的数据为客户端和服务器之间传输的专用存储数据的名字。

#from...import...不需要加模块名
#import...需要加模块名
import socket
#(1)创建socket对象
socket_obj=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#(2)绑定IP和端口号
socket_obj.bind(('127.0.0.1',8888))
#(3)开始监听
socket_obj.listen(5)#最多同时等待5个客户端连接
#(4)等待服务器连接
client_socket,client_addr=socket_obj.accept()#client_addr:客户端ip+端口.client_socket:客户端与服务器建立的通信通道
#(5)接收数据
info=client_socket.recv(1024).decode('utf-8')
#(6)准备执行多次通信
while info!='bye':#初始化变量info,循环条件
    if info!='':
        print('接收到的数据是',info)
    data=input('请输入要发送的数据:')
    client_socket.send(data.encode('utf-8'))#由客户端发送过来接受并由服务器端向客户端发送client_socket的数据
    if data=='bye':#对发送的数据再次做判断
        break
    info=client_socket.recv(1024).decode('utf-8')#改变变量,对服务器端接收到的数据并再次做判断
#(7)关闭对象
socket_obj.close()
import socket
client_socket=socket.socket()
client_socket.connect(('127.0.0.1',8888))#一旦服务器与客户端建立连接,所有收发数据必须用client_socket完成
print('已经与服务器成功建立联系')
info=''
while info!='bye':
    send_data=input('请客户端输入要发送的数据:')
    client_socket.send(send_data.encode('uft-8'))
    if send_data=='bye':
        break
    info=client_socket.recv(1024).decode('uft-8')
    print('收到服务器的响应数据:',info)
client_socket.close()

4.UDP的一次双向通信

与TCP的整体思路相同,但要用sendto和recvfrom来传输和接收数据,发送和接收数据时要记得指定接收方的IP地址和端口

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)#sendto(data,(ip,port))
#接受来自接收方的回复数据
recv_data,addr=send_socket.recvfrom(1024)#需要以元组(data,adress)类型进行接收
print('接收到的数据为:',recv_data.decode('utf-8'))
#(5)关闭socket对象
send_socket.close()
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()

5.模拟客服咨询的小程序

使用while循环来进行多次通信,服务人员要先接收数据判断接收到的数据并决定是否终止通信,若为中断则要准备回复对方的数据,接收的数据和发送的数据协议要相同且编码形式也要一样。

from socket import socket,AF_INET,SOCK_DGRAM
#编写socket对象
recv_socket=socket(AF_INET,SOCK_DGRAM)
#绑定IP地址和端口
recv_socket.bind(('127.0.0.1',8888))
while True:#必须用break终止语句
    #接受发送过来的数据
    recv_data,addr=recv_socket.recvfrom(1024)
    print('客户说:',recv_data.decode('utf-8'))
    if recv_data.decode('utf-8')=='bye':
        break
    #准备回复对方的数据
    data=input('客服回:')
    recv_socket.sendto(data.encode('utf-8'),addr)
recv_socket.close()
from socket import socket,AF_INET,SOCK_DGRAM
send_socket=socket(AF_INET,SOCK_DGRAM)
ip_port=('127.0.0.1',8888)
while True:
    data=input('客户说:')
    send_socket.sendto(data.encode('utf-8'),ip_port)
    if data=='bye':
        break
    recv_data,addr=send_socket.recvfrom(1024)
    print('客服回:',recv_data.decode('utf-8'))
send_socket.close()

三.进程与线程

1.基础概念认识

(1)进程:是指启动后的程序,系统会为进程分配内存空间。在进程中,每个进程的内部数据和状态都是完全独立的。一个进程就是一个执行中的程序,而每个进程都有自己独立的一块内存空间和一组系统资源

(2)程序:是指一系列有序指令的集合,使用编程语言所编写,用于实现一定的功能。

(3)线程:是CPU可调度的最小单位,被包含在进程中,是进程中实际的运作单位。一个进程中可以拥有N多个线程并行执行,而每个线程并行执行不同的任务

(4)进程池:创建一个进程池,并设置进程池中最大的进程数量。一次可执行多次任务。

(5)并发:指两个或多个事件统一时间间隔发生,多个任务被交替轮换这执行。吃完苹果拿快递,拿完快递吃苹果。

(6)并行:指两个或多个事件在同一时刻发生,多个任务在同一时刻在多个处理器上同时执行。三件事同时干。

(7)队列:先进先出的数据结构,进程之间可以通过队列进行通信。

2.创建进程和线程以及进程池的方式

创建进程的方式:

(1)process(group=None,target,name,args,kwargs)

Group:表示分组,实际上不使用,默认为None

Target:表示子进程要执行的任务,支持函数名

Name:表示子进程的名称

Args:表示调用函数的位置参数,以元组的形式传递

Kwards:表示调用函数的关键字参数,以字典的形式进行传递

(2)class 子进程(Process):

Pass

(3)创建进程池:进程池对象=Pool(N)

(4)创建队列:队列对象=Queue(N)

(5)创建线程t=Thread(group,target,name,args,kwargs)

group:创建线程对象的进程组

target:创建的线程对象所要执行的目标函数

name:创建线程对象的名称,默认为“Tread-n”

args:用元组以位置参数的形式传入target对应函数的参数

kwargs:用字典以关键字参数的形式传入target对应函数的参数

(6)自定义模块下的类

   实现run方法

3.函数式创建子进程

定义函数,该函数为子进程要执行的内容,紧接着创建if __name__=='__main__':,主进程开始进行,用空列表来储存子进程,在遍历循环中,p=Process(target=test)创建子进程,并在循环中启动子进程,将子进程添加到列表中,其中遍历列表中的五个子进程,遍历阻塞主进程,每个子进程都要监听到阻塞行为才能确保主进程成功阻塞。正常情况下主进程先执行完子进程后执行完,改变执行先后顺序,需要用join()。

from multiprocessing import Process
import os,time
#函数中的代码是进程要执行的代码
def test():
    print(f'我是子进程,我的PIP是{os.getpid()},我的父进程是{os.getppid()}')
    time.sleep(1)
if __name__=='__main__':
    print('主进程开始执行')
    lst=[]
    #创建五个子进程
    for i in range(5):
        #创建子进程
        p=Process(target=test)
        #启动子进程
        p.start()
        #将启动中的程序添加到列表中
        lst.append(p)
    #遍历lst,列表中的五个子进程
    for item in lst:#item类型为Process类
        item.join()#遍历阻塞主进程,每个子进程都要监听到阻塞行为才能确保主进程成功阻塞
    print('主进程执行结束')
#正常情况下主进程先执行完子进程后执行完,改变执行先后顺序,需要用join()

4.Process类中常用的属性和方法

name

当前进程实例别名,默认为Process-N

pid

当前进程对象的PID值

is_alive()

进程是否执行完,没执行完结果为true,否则为false

join(timeout)

等待结束或等待timeout秒

start()

启动进程

run()

如果没有指定target参数,则启动进程后,会调用父类中的run方法

terminate()

强制终止进程

from multiprocessing import Process
import os,time
#函数式方式创建子进程
def sub_process(name):
    print(f'子进程PID:{os.getpid()},父进程{os.getppid()}---------{name}')
    time.sleep(3)
def sub_process2(name):
    print(f'子进程PID:{os.getpid()},父进程{os.getppid()}---------{name}')
    time.sleep(3)
if __name__=='__main__':
    #主进程
    print('主进程开始执行')
    for i in range(3):
        #创建子进程
        p1=Process(target=sub_process,args=('lcx',))#若不传参,则会调用执行run不执行子进程
        p2=Process(target=sub_process2,args=(19,))#如果只有一个参数元组中也要有逗号
        #启动子进程
        p1.start()
        p2.start()
        print(p1.name,'是否执行完毕',p1.is_alive())
        print(p2.name,'是否执行完毕',p2.is_alive())
        print(p1.name,'PID是:',p1.pid)
        print(p2.name,'PID是:',p2.pid)
        p1.join()
        p2.join()
        print(p1.name, '是否执行完毕', p1.is_alive())
        print(p2.name, '是否执行完毕', p2.is_alive())#执行过后不再活跃
        #终止进程
        #p1.terminate()
    print('父进程执行完毕')

5.继承式创建子进程

与类中子类和父类的继承相似,通过继承父类Process来调用函数,创建子进程。

from multiprocessing import Process
import os,time
#自定义一个类
class SubProcess(Process):
    #编写一个初始化方法
    def __init__(self,name):
        #调用父类中的初始化方法
        super().__init__()
        self.name=name
    #重写父类中的run方法
    def run(self):
        print(f'子进程的名称{self.name},PID:{os.getpid()},父进程的PID:{os.getppid()}')
if __name__=='__main__':
    print('父进程开始执行')
    lst=[]
    for i in range(5):
        p1=SubProcess(f'进程:{i}')
        #启动进程
        p1.start()
        lst.append(p1)
    #阻塞一下主进程
    for item in lst:
        item.join()
        print('父进程执行结束')

6.进程池的使用

apply_async(func,args,kwargs)

使用非阻塞方式调用函数func

apply(func,args,kwargs)

使用阻塞方式调用函数func

close()

关闭进程池,不再接收新任务

terminate()

不管任务是否完成,立即终止

join()

阻塞主进程,必须在terminate()或close()后使用

from multiprocessing import Pool
import time,os
def task(name):
    print(f'子进程的pid{os.getpid()},执行的任务{name}')
    time.sleep(2)
if __name__=='__main__':
    start=time.time()
    print('父进程开始执行')
    #创建进程池
    p=Pool(3)#同时执行三个任务
    #创建任务
    for i in range(10):
        #以非阻塞的方式
        p.apply_async(func=task,args=(i,))#apply阻塞子进程
    p.close()#关闭进程池不再接收新任务
    p.join()  # 阻塞父进程,等待所有的子进程执行完毕后,才会执行父进程中的代码。若无该条件则会直接退出进程池结束父进程不执行子进程
    print('所有子进程执行完毕,父进程执行结束')
    print(time.time()-start)

7.进程之间的数据不共享

通过设置两个子进程和一个全局变量,整体执行完毕后,全局变量的值不受影响,且子进程1执行后对子进程2的执行无影响

from multiprocessing import Process
a=100
def add():
    print('子进程1开始执行')
    global a#全局变量声明
    a+=30
    print('a=',a)
    print('子进程1执行完毕')
def sub():
    print('子进程2开始执行')
    global a  # 全局变量声明
    a-=30
    print('a=',a)
    print('子进程2执行完毕')
if __name__ == '__main__':
    print("父进程开始执行")
    print('a=', a)
    p1=Process(target=add)
    p2=Process(target=sub)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('父进程执行结束')
    print('a=',a)#执行结束后没变

8.队列的基本使用

队列为一进一出,第一个进第一个出,也可以用来判断队列中的信息

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_nowwait(item)

相当于put(item,block=False)

from multiprocessing import Queue
if __name__ == '__main__':
    #创建一个队列
    q = Queue(3)#调用类名创建对象,最多可以接收3条信息
    print('队列是否为空',q.empty())
    print('队列是否为满', q.full())
    #向队列中添加信息
    q.put('hello')
    q.put('world')
    print('队列是否为空', q.empty())
    print('队列是否为满', q.full())
    q.put('python')
    print('队列是否为空', q.empty())
    print('队列是否为满', q.full())
    print('队列当中信息个数', q.qsize())
    #出队
    print( q.get())#将第一个入对的拿出
    print('队列当中信息个数', q.qsize())
    #入队
    q.put_nowait('pycharm')#不等待入队
    #q.put('world')#一直等待入队
    #遍历队内
    if not q.empty():
        for i in range(q.qsize()):
            print(q.get_nowait())#不等待出队
    print('队列是否为空', q.empty())
    print('队列是否为满', q.full())
    print('队列当中信息个数', q.qsize())

9.使用队列实现进程之间的通信

首先将数据传入队列中,并编写函数出队,通过观看运行结果可知先入队的先出队。

from multiprocessing import Queue, Process
import time
a=100
def write_msq(q):#q队列并将其作为参数传入
    global a
    if not q.full():
        for i in range (6):
            a-=10
            q.put(a)
            print('此时a的值为:',a)
#出队 写和出用的同一个队列
def read_msq(q):
    time.sleep(2)
    while not q.empty():
        print('出队时a的值:',q.get())
if __name__ == '__main__':
    print('父进程开始执行')
    q=Queue()#由父进程创建队列,没有指定参数,说明可接受消息的个数没有上线
    #创建两个子进程
    p1=Process(target=write_msq, args=(q,))
    p2=Process(target=read_msq, args=(q,))
    #启动两个子进程
    p1.start()
    p2.start()
    #等待写的进程执行完毕,再去执行主进程
    print('父进程执行完毕')

10.函数式创建线程

编写函数,创建子进程,启动子进程。主线程负责执行 main中的代码,Thread-1线程执行三次循环,Thread-2线程执行三次循环。三个线程又是并发执行,三个线程并行执行test函数。因此谁先执行取决于CPU的调度。

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}')
    #主线程负责执行 main中的代码,Thread-1线程执行三次循环,Thread-2线程执行三次循环
    #三个线程又是并发执行,三个线程并行执行test函数

11.继承式创建线程

与继承式创建子进程的流程相近

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__':
    print('主线程开始执行')
    lst=[SubThread() for i in range(2)]
    for item in lst:
        item.start()
    for item in lst:
        item.join()
    print('主线程执行完毕')

12.线程之间的数据共享

通过创建两个子线程进行运行,发现全局变量在子线程1的执行下影响到子线程2的执行结果了,最终主程序执行完毕后,a的值也发生了改变。

from threading import Thread
a=100
def add():
    print('加线程开始执行')
    global a
    a+=10
    print(f'a的值为{a}')
    print('加线程执行完毕')
def sub():
    print('减线程开始执行')
    global a
    a-=50
    print(f'a的值为{a}')
    print('减线程执行完毕')
if __name__=='__main__':
    print('主线程开始执行')
    print(f'a的值为{a}')
    add=Thread(target=add,args=())
    sub=Thread(target=sub,args=())
    add.start()
    sub.start()
    add.join()
    sub.join()
    print('主线程执行结束')
    print(f'a的值为{a}')

13.多个线程共享数据带来的问题

由于线程在执行时是并行,在调用函数时并发,因此会出现刚子线程1调用,子线程2也正在调用,词用要用lock模块对其进行上锁,防止其他子线程对其调用。

import threading#模块
from threading import Thread,Lock#从模块中导入类
import time
ticket=50#一共有50张票
lock_obj=Lock()#创建锁对象
def sale_ticket():#线程所要执行的
    global ticket
    #每个排队窗口假设有100人
    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()#启动线程

14.生产者与消费者问题

生产数据并存放到数据库,消费者要从数据库中取出数据

from threading import Thread
from queue import Queue
import time
#创建一个生产者类
class Producer(Thread):
    def __init__(self,name,queue):
        Thread.__init__(self,name=name)
        self.queue = queue
    def run(self):
        for i in range(1,6):
            print(f'{self.name}将产品{i}放入队列')
            self.queue.put(i)
            time.sleep(1)
        print('生产者完成了所有数据的存储')
#创建一个消费者类
class Consumer(Thread):
    def __init__(self,name,queue):
        Thread.__init__(self,name=name)
        self.queue = queue
    def run(self):
        for _ in range(5):
            value=self.queue.get()
            print(f'消费者线程:{self.name}取出了{value}')
        print('消费者线程完成了所有数据的取出')
if __name__ == '__main__':
    #创建队列
    queue = Queue()
    #创建生产者线程
    p=Producer('Producer',queue)
    #创建消费者线程
    c=Consumer('Consumer',queue)
    #启动线程
    p.start()
    c.start()
    p.join()
    c.join()

四.实战练习

1.车牌归属地

温习正则表达式,for循环,format格式化的使用,索引的使用

import re
lst=[]
for i in range(3):
    plate_number=input('请输入您的车牌号:')
    pattern = '\D\D\d*'
    if re.findall(pattern,plate_number):
        lst.append(plate_number)
for num in lst:
    area=num[0:1]
    print(f'{num}归属地为{area}')

2.提取字符串中所有数字并求和

import re
x=input('请输入一个字符串:')
print(x)
num=re.findall('\d',x)
print('提取到的数字列表为:',num)
y=0
for i in num:
    y+=int(i)
print('累加和为:',y)
print('-------------------------------------------')
def get_isdigit(s):
    a=0
    lst=[]
    for item in s:
        if item.isdigit():
            lst.append(int(item))
    a=sum(lst)
    return a,lst
a=input('请输入一个字符串:')
s,lst=get_isdigit(x)#解包
print('提取的数字列表为:',lst)
print('累加和为:',s)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐