一、面向对象的基本概念
1.面向过程与面向对象
- 面向过程:关注事物发展的过程进行编程,程序通过模拟事物发展的过程实现最终效果。
- 面向对象:关注事物之间的关系进行编程,程序通过模拟事物之间的交互实现最终效果。
2.面向对象的三大特性
- 封装:隐藏对象的属性和方法,仅对外提供接口供外界访问
- 使用者只需要通过接口访问,无需了解对象的细节
- 对象可以设置访问的权限,限制访问的内容
- 继承:子类继承父类的属性和方法
- 子类具有父类的特征和行为
- 子类可以额外拥有自己的特征和行为
- 多态:同一函数在不同环境下表现出不同的状态
二、类和对象
1.基本概念
- 类:将事物进行抽象得到的模板(例如:空的个人信息表)
- 对象:根据模板填充过具体的属性的实体(例如:填写过的个人信息表)
2.定义方法
- 类的定义
#类的定义 #定义方法一 """ 定义格式: class 类名 (功能列表) def 函数1 def 函数2 ... """ #例:学生类 class Student1: def getType(self): print('这是一个学生') #定义方法二 """ 定义格式: class 类名() (功能列表) def 函数1 def 函数2 ... """ #例:学生类 class Student2(): def getType(self): print('这是一个学生') #定义方法三 """ 定义格式: class 类名(object) (功能列表) def 函数1 def 函数2 ... """ #例:学生类 class Student3(object): def getType(self): print('这是一个学生')
- 对象的定义
#对象的定义 """ 在定义类的基础上: 创建对象:对象名=类() 调用对象:对象名.方法名() """ #例:使用学生类 xiaoming = Student() xiaoming.getType() #一个类可以创建多个对象 xiaohong = Student() xiaohong.getType()
3.self关键字
- 内容
- 每一个对象拥有一个独特的self值,这个值标识了不同的对象
- 作用
- self区分了不同的对象,对象在调用类方法时,会传入self值以便类区分是哪一个对象在调用方法
- 在类的内部调用类的方法:self.方法名()
4.对象的属性
- 操作属性
- 在类的外部操作属性
#在类的外部操作属性 """ 设置属性:对象名.属性=属性值 调用属性:对象名.属性 """ class Student: def getType(self): print('这是一个学生') xiaoming = Student() xiaoming.name = 'xiaoming' xiaoming.age = 18 print(f'小明的年龄是{xiaoming.age}岁')
- 在类的内部操作属性
#在类的内部操作属性 """ 调用属性:self.属性 """ class Student: def getName(self): print(f'学生的姓名是{self.name}') xiaoming = Student() xiaoming.name = '小明' xiaoming.getName()
5.魔法方法
- 概念
- 魔法方法的命名特点是
__方法名__
,魔法方法在满足条件时会自动调用
- 魔法方法的命名特点是
__init__
- 条件:通过类创建对象时,若类中存在
__init__
函数,则会自动调用 - 返回值:None
- 使用
- 【无参版本】
def __init__(self):
#无参__init__ class Student: def __init__(self): self.name = '未命名' def getName(self): print(self.name) xiaoming = Student() xiaoming.getName() print(xiaoming.name)
- 【有参版本】
def __init__(self,参数1,参数2,...)
#有参__init__ class Student: def __init__(self,name): self.name = name def getName(self): print(self.name) xiaoming = Student('小明') xiaoming.getName() print(xiaoming.name)
- 条件:通过类创建对象时,若类中存在
__str__
- 条件:通过print输出对象时,默认打印对象的内存地址,若类中存在
__str__
函数,则会自动调用 - 返回值:str类型
- 使用:
def __str__(self)
#__str__ class Student: def __init__(self,name): self.name = name def __str__(self): return f'学生姓名:{self.name}' xiaoming = Student("小明") print(xiaoming)
- 条件:通过print输出对象时,默认打印对象的内存地址,若类中存在
__del__
- 条件:当删除对象时,默认调用
__del__
函数 - 使用:
def __del__(self)
#__del__ class Student: def __del__(self): print('调用了__del__') xiaoming = Student() del xiaoming xiaohong = Student() #程序结束默认调用__del__
- 条件:当删除对象时,默认调用
三、继承
1.基本概念
- 继承:指子类可以继承父类的属性和方法(站在子类视角)
- 派生:指从已有的类产生一个新的类(站在父类视角)
- 注意:继承是类的继承,而并非对象的继承
2.定义方法
#继承的定义
"""
class 类名(父类名):
"""
class Father:
def __init__(self):
self._money = 100
def printHobby(self):
print("Computer")
class Son(Father):
pass
xiaoming = Son()
xiaoming.printHobby()
2.单继承与多继承
- 单继承:子类单独继承一个父类,仅能继承该父类的属性和方法
#单继承 class Master: def __init__(self): self._skill='<降龙十八掌>' def getSkill(self): print(f'使用了{self._skill}') class Prentice(Master): pass xiaoming = Prentice() xiaoming.getSkill()
- 多继承:子类从多个父类进行继承,可以继承所有父类的属性和方法
# 多继承 class Master1: def __init__(self): self._skill = '<降龙十八掌>' def getSkill(self): print(f'使用了{self._skill}') class Master2: def __init__(self): self._skill = '<暴雨梨花针>' def getSkill(self): print(f'使用了{self._skill}') class Prentice(Master1, Master2): pass xiaoming = Prentice() #同名属性和方法按照调用顺序来执行 print(xiaoming._skill) xiaoming.getSkill() #查看调用顺序的方法 print(Prentice.__mro__) #返回元组 print(Prentice.mro()) #返回列表 """ 运行结果: (<class '__main__.Prentice'>, <class '__main__.Master1'>, <class '__main__.Master2'>, <class 'object'>) 表明: 1.同名属性和方法按照Prentice,Master1,Master2,object的顺序进行调用 2.若按照上述顺序未能成功找到,则会报错 """
3.子类重写父类方法
#子类重写父类同名属性和方法
class Master1:
def __init__(self):
self._skill = '<降龙十八掌>'
def getSkill(self):
print(f'使用了{self._skill}')
class Master2:
def __init__(self):
self._skill = '<暴雨梨花针>'
def getSkill(self):
print(f'使用了{self._skill}')
class Prentice(Master1, Master2):
def __init__(self):
self._skill='<降龙暴雨掌>'
def getSkill(self):
print(f'使用了{self._skill}')
xiaoming=Prentice()
xiaoming.getSkill()
4.子类调用父类方法
- 显式调用
#显式调用 class Master: def __init__(self): self._skill = '<降龙十八掌>' def getSkill(self): print(f'使用了{self._skill}') class Prentice(Master): def __init__(self): self._skill='<降龙暴雨掌>' def getSkill(self): print(f'使用了{self._skill}') def getMasterSkill(self): Master.__init__(self) Master.getSkill(self) xiaoming=Prentice() xiaoming.getSkill() xiaoming.getMasterSkill()
- super(子类名,self):可以调用子类的父类
#super()调用 #适用于单继承的情况 class Master: def __init__(self): self._skill = '<降龙十八掌>' def getSkill(self): print(f'使用了{self._skill}') class Prentice(Master): def __init__(self): self._skill='<降龙暴雨掌>' def getSkill(self): print(f'使用了{self._skill}') def getMasterSkill(self): super().__init__() super().getSkill() xiaoming=Prentice() xiaoming.getSkill() xiaoming.getMasterSkill()
四、封装
1.基本概念
- 封装:将属性和方法写入类中
2.私有方法与私有属性
- 私有属性和私有方法不继承给子类
#私有方法与私有属性 """ 规则: 1.只能在类的内部使用 2.通过公共方法的外部接口在类外使用 定义: __变量名 """ class Father: def __init__(self): self.skill='<武林秘籍>' self.__money=50000 #私有属性 def use_money(self,money): #通过公共方法访问私有属性 self.__money-=money def check_money(self): print(self.__money) class Son(Father): pass xiaoming=Son() xiaoming.check_money() xiaoming.use_money(1000) xiaoming.check_money()
五、多态
1.基本概念
- 多态:指同样的函数在不同场景下具有不同的状态
2.使用方法
"""
实现多态的三个条件:
1.有继承(存在父类,子类)
2.函数重写(子类重写父类的函数)
3.父类引用指向子类对象(子类对象传给父类对象调用者)
"""
"""
==在Python下的简单理解,就是将类名作为参数传递==
1.函数列表中存在一个形参,这个形参是“通用类名”,当传入不同的类时,可以简单认为直接将形参出现的位置全部替换为传入的类名
(类似于数学函数中代入未知数x的值,可以认为是将x出现的位置全部替换为该值)。
2.在函数体中,我们用这个形参去调用一个类的函数,例如调用get函数,当这个“通用类名”传入不同的类名时,就会发生替换,指向该类名的get函数
(传入Person就是Person.get,传入Worker就是Worker.get,传入Student就是Student.get)
"""
class Person:
def get(self):
print('人类')
class Worker(Person):
def get(self):
print('工人')
class Student(Person):
def get(self):
print('学生')
def who(person:Person):
person.get()
people1=Person()
people2=Worker()
people3=Student()
who(people1)
who(people2)
who(people3)
3.抽象类和抽象方法
- 定义方法:在函数体中使用pass
""" 抽象类就是定义了一个标准,抽象方法就是标准中的具体条目 抽象类的子类必须按照标准实现对应的方法 """ class Person: def get_age(self): pass class Student: def __init__(self,age): self.__age=age def get_age(self): print(self.__age)
4.类属性与类方法
#类属性与类方法
"""
类方法的定义:
使用装饰器'@classmethod'标识类方法,类方法的第一个参数必须为类对象,通常为cls
类方法的调用:
类名.方法名
对象名.类方法名
静态方法:
使用装饰器@staticmethod标识静态方法
静态方法的调用:
类名.静态方法名
对象名.静态方法名
"""
class Person:
@classmethod
def get_money(cls):
print('赚钱中...')
@staticmethod
def get_race():
print('人类')
count=1
p1=Person()
p2=Person()
#1.类属性
print(Person.count)
print(p1.count)
print(p2.count)
#对象之间的属性是独立的
p1.count+=1
print(p1.count)
print(p2.count)
#2.类方法
Person.get_money()
p1.get_money()
#3.静态方法
Person.get_race()
p1.get_race()
5.回调
- 回调:程序员调用框架时,框架回头调用程序员实现的函数
六、赋值与拷贝
1.引用赋值
- Python中的变量使用引用赋值,变量实际上是对具体数值的引用
- 也就是说,在Python中变量存储的是地址信息,在传递参数时,传递的也是地址信息
2.浅拷贝与深拷贝
- 拷贝可变数据类型
- Python中浅拷贝可变数据类型时,只拷贝第一层数据并开辟空间存储,其它层不拷贝
- Python中深拷贝可变数据类型时,会拷贝多层数据直到可变数据类型的最深一层,并开辟空间存储
- 拷贝不可变数据类型
- Python中拷贝不可变数据类型,无论是深拷贝还是浅拷贝都是进行引用赋值
七、进阶函数使用
1.函数名的作用
- 函数名存储的是函数所在空间的地址
- 函数名()执行函数名所存放空间地址中的代码
- 函数名可以作为函数参数
2.直接调用和间接调用
def func():
print('func is called')
#1.直接调用
func()
#2.间接调用
def test(function):
function()
test(func)
"""
将func作为实参传入function,实际上是传入了一个函数地址
再使用()执行地址中的代码
"""
3.闭包
- 闭包的作用是保存函数中的变量,使其在函数销毁时不会被销毁,可以继续使用
#闭包的语法 """ def 外部函数(): def 内部函数(): 调用外部函数的变量 return 内部函数名 闭包的三个条件: (1)存在函数嵌套 (2)内部函数调用外部函数的变量 (3)返回内部函数的函数名 """ def func1(num1): #num1=5 #在内部定义变量也可以,但作为形参的好处是可以从实参那里修改变量值 def func2(num2): #nonlocal num1 #通过这种方式可以获得外部函数变量的修改权限 sum = num1 + num2 print(f'sum={sum}') return func2 #第一次调用返回func2的地址,此时定义了func2,func2中调用的func1的变量数值不变了 myfunc1 = func1(10) #再次调用时会使用func2的函数体 myfunc1(1)
4.装饰器
- 装饰器的基本语法
#装饰器 #装饰器本质上是一个闭包 def check(func): def inner(): print("请先验证") func() return inner #1.方式一 """ 装饰器(函数名) """ def func1(): print('执行功能:func1') f1 = check(func1) f1() #2.方式二 """ @装饰器 def 函数名() """ @ check def func2(): print('执行功能:func2') func2()
- 不同格式函数的装饰方法
#不同函数的装饰器使用方法 """ inner与被装饰函数格式保持一致!!! """ #1.无参无返回值 def print_info_1(func): def inner(): print('正在加载中') func() return inner @ print_info_1 def func1(): print('func1被调用') func1() #2.无参有返回值 def print_info_2(func): def inner(): print('正在加载中') return func() #被装饰函数有返回值,这里需要返回 return inner @ print_info_2 def func2(): print('func2被调用') return 0 print(func2()) #3.有参有返回值 def print_info_3(func): def inner(num): #被装饰函数有参数,这里要加参数 print('正在加载中') return func(num) return inner @ print_info_3 def func3(num1): print('func3被调用') return num1 print(func3(5)) #4.不定长参数函数 def print_info(func): def inner(*args,**kwargs): print('正在加载中') func(*args,**kwargs) return inner @ print_info def function(*args,**kwargs): print(*args,**kwargs) function(1,2,3,4,5,6,7)
- 多个装饰器装饰同一函数
#多个装饰器装饰一个函数 """ (1)decorator2会先装饰function,此时形成一个整体 (2)decorator1随后装饰这个整体,形成新的大整体 (3)运行时,先运行decorator1,随后运行decorator2,最后运行function """ def decorator1(func): def inner(): print('初始化中...') func() return inner def decorator2(func): def inner(): print('模块加载中...') func() return inner @ decorator1 @ decorator2 def function(): print('程序运行中') function()
- 带参数的装饰器
#带参数的装饰器 def flag_func(flag): def decorator(func): def inner(a,b): if flag=='+': print('正在进行加法运算') elif flag=='-': print('正在进行减法运算') return func(a,b) return inner return decorator @ flag_func('+') def sum(a,b): return a+b print(sum(1,2)) @ flag_func('-') def decrease(a,b): return a-b print(decrease(1,2))
八、网络编程
1.网络编程三要素
- IP:网络环境下每一台计算机的唯一标识,可以通过IP地址找到指定的计算机
- 端口:用于标识进程的逻辑地址,通过端口找到指定的进程
- 协议:通信的规则,符合协议允许进行通信
2.socket
#导入socket模块
import socket
#创建socket对象
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
"""
socket.AF_INET:表示使用IPv4协议族
socket.SOCK_STREAM:表示使用面向连接的套接字
两者共同确定使用的协议是IPPROTO_TCP协议
"""
print(tcp_socket)
3.TCP连接
- 服务端
import socket #1.创建套接字 server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # print(server_socket) #2.绑定端口 ip = '127.0.0.1' port = 8888 server_socket.bind((ip,port)) #3.监听端口 print('服务器监听中...') server_socket.listen() #此时server_socket成为被动套接字用于连接 #4.接收请求 connect_socket,client_ip = server_socket.accept() #此时connect_socket成为传输套接字,用于向客户端传输 #client_ip为客户端的IP地址 #5.接收数据 recv_data = connect_socket.recv(1024) #接收到的数据是二进制数据,不可见,需要进行转换 print(recv_data.decode(encoding='utf-8')) #6.返回数据 connect_socket.send('服务器已接收'.encode(encoding='utf-8')) #7.关闭套接字 connect_socket.close() #关闭连接套接字 server_socket.close() #关闭监听套接字
- 客户端
import socket #1.创建套接字 client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #2.连接服务器 ip = '127.0.0.1' port = 8888 client_socket.connect((ip,port)) #3.发送数据 client_socket.send('客户端信息测试'.encode(encoding='utf-8')) #4.接收数据 recv_data = client_socket.recv(1024) print(recv_data.decode(encoding='utf-8')) #5.关闭套接字 client_socket.close()
- 端口复用
#将server.socket套接字的端口可以进行复用 server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,TRUE)
九、多任务开发——进程
1.并发与并行
- 并发:在一段时间内,交替地执行程序(单个CPU)
- 并行:在一段时间内,同时地执行程序(多个CPU)
2.进程
- 进程:进程是CPU分配资源的最小单位,是正在运行的程序
- 程序:是一段代码,表明了一段业务逻辑,并没有被执行
- 同一个程序可以产生多个进程,例如同时开启多个QQ
3.多进程
- 导包
import multiprocessing
- 创建进程对象
multiprocessing.Process(group=None,target=None,name=None,args=(),kwargs={}) """ group:参数未使用,值始终为None target:表示要调用的对象,传入函数地址,生成子任务函数 args:表示以元组的形式向子任务函数传递参数 kwargs:表示以字典的形式向子任务函数传递参数 name:设置子进程的名称 """
- 多进程(无参)
import time #导入多进程模块 import multiprocessing def func1(): for i in range(100): print(f'func1运行中...{i+1}%') time.sleep(0.1)#设置休眠便于观察,与多进程无关 def func2(): for i in range(100): print(f'func2运行中...{i+1}%') time.sleep(0.1) #注意:多进程的代码必须写入“if __name__ == '__main__':"中 if __name__ == '__main__': #1.创建子进程对象 p1 = multiprocessing.Process(target=func1,name='p1') p2 = multiprocessing.Process(target=func2,name='p2') #2.启动子进程 p1.start() p2.start() #3.设置主进程阻塞,等待子进程完成 p1.join() p2.join() print('主进程结束')
- 多进程(有参)
import time #导入多进程模块 import multiprocessing def func1(name): for i in range(100): print(f'欢迎您{name},func1运行中...{i+1}%') time.sleep(0.1)#设置休眠便于观察,与多进程无关 def func2(name): for i in range(100): print(f'欢迎您{name},func2运行中...{i+1}%') time.sleep(0.1) #注意:多进程的代码必须写入“if __name__ == '__main__':"中 if __name__ == '__main__': #1.创建子进程对象 p1 = multiprocessing.Process(target=func1,name='p1',args=('小明',))#元组中只有一个元素,需要加',' p2 = multiprocessing.Process(target=func2,name='p2',kwargs={'name':'小红'}) #2.启动子进程 p1.start() p2.start() #3.设置主进程阻塞,等待子进程完成 p1.join() p2.join() print('主进程结束')
- 获取进程编号
- 获取当前进程编号
#方式1:使用os模块 import os pid = os.getpid() #方式2:使用multiprocessing模块 import multiprocessing pid = multiprocessing.current_process().pid
- 获取父进程编号
import os ppid = os.getppid()
4.注意点
import multiprocessing
my_list = []
print('这是一个全局打印')
def func1():
for i in range(10):
my_list.append(i)
print(my_list)
def func2():
for i in range(10):
my_list.append(i*10)
print(my_list)
if __name__ == '__main__':
p1 = multiprocessing.Process(target=func1)
p2 = multiprocessing.Process(target=func2)
p1.start()
p2.start()
p1.join()
p2.join()
#主进程输出my_list,结果为空表
print(my_list)
"""
操作系统在创建子进程时,会将主进程的资源拷贝给子进程
子进程中的修改时对自身拷贝的资源进行修改,对主进程的资源没有影响
子进程会拷贝主进程的所有代码
但因为子进程中的__name__ != '__main__',因此不会执行其中的内容
如果在主进程中不使用”if __name__ == '__main__':"
那么子进程也会创建子进程,陷入死循环
"""
5.改变子进程退出方式
- 主进程默认会等待所有子进程结束之后再销毁
- 不让主进程等待子进程
- 方式一:设置守候进程
import time import multiprocessing def func1(): for i in range(100): print(f'func1运行中...{i+1}%') time.sleep(0.1) def func2(): for i in range(100): print(f'func2运行中...{i+1}%') time.sleep(0.1) if __name__ == '__main__': p1 = multiprocessing.Process(target=func1,name='p1',daemon=True)#将daemon设置为True p2 = multiprocessing.Process(target=func2,name='p2',daemon=True) p1.start() p2.start() time.sleep(2) print('主进程结束') """ 设置守候进程时: 主进程结束之后,子进程会被系统接管 """
- 方式二:暴力直接结束子进程
import time import multiprocessing def func1(): for i in range(100): print(f'func1运行中...{i+1}%') time.sleep(0.1) def func2(): for i in range(100): print(f'func2运行中...{i+1}%') time.sleep(0.1) if __name__ == '__main__': p1 = multiprocessing.Process(target=func1,name='p1') p2 = multiprocessing.Process(target=func2,name='p2') p1.start() p2.start() time.sleep(2) p1.terminate() #直接结束 p2.terminate() print('主进程结束') """ 直接结束子进程,不建议使用!!! 此时子进程占用的资源没有被回收,成为了僵尸进程!!! """
十、多任务开发——线程
1.线程
- 线程是CPU调度资源的最小单位
2.多线程
- 导入线程模块
import threading
- 创建线程对象
线程对象 = threading.Thread(target=任务名)
- 启动线程
线程对象.start()
- 多进程(无参)
import time #导入线程模块 import threading def func1(): for i in range(100): print(f'func1运行中...{i+1}%') time.sleep(0.1) def func2(): for i in range(100): print(f'func2运行中...{i+1}%') time.sleep(0.1) if __name__ == '__main__': #1.创建线程对象 th1 = threading.Thread(target=func1) th2 = threading.Thread(target=func2) #2.启动线程 th1.start() th2.start() #3.主线程阻塞等待 th1.join() th2.join() print('主线程结束')
- 多进程(带参)
import time #导入线程模块 import threading def func1(name): for i in range(100): print(f'当前用户{name}:func1运行中...{i+1}%') time.sleep(0.1) def func2(name): for i in range(100): print(f'当前用户{name}:func2运行中...{i+1}%') time.sleep(0.1) if __name__ == '__main__': #1.创建线程对象 th1 = threading.Thread(target=func1,args=('Jack',)) th2 = threading.Thread(target=func2,kwargs={'name':'Tom'}) #2.启动线程 th1.start() th2.start() #3.主线程阻塞等待 th1.join() th2.join() print('主线程结束')
3.线程间执行顺序是无序的
import time
#导入线程模块
import threading
def func1(name):
for i in range(100):
print(f'当前用户{name}:func1运行中...{i+1}%')
time.sleep(0.1)
def func2(name):
for i in range(100):
print(f'当前用户{name}:func2运行中...{i+1}%')
time.sleep(0.1)
if __name__ == '__main__':
#1.创建线程对象
th1 = threading.Thread(target=func1,args=('Jack',))
th2 = threading.Thread(target=func2,kwargs={'name':'Tom'})
th3 = threading.Thread(target=func1,args=('Mike',))
th4 = threading.Thread(target=func2,kwargs={'name':'Lisa'})
th5 = threading.Thread(target=func1,args=('Lily',))
th6 = threading.Thread(target=func2,kwargs={'name':'Jhon'})
#2.启动线程
th1.start()
th2.start()
th3.start()
th4.start()
th5.start()
th6.start()
#3.主线程阻塞等待
th1.join()
th2.join()
th3.join()
th4.join()
th5.join()
th6.join()
print('主线程结束')
"""
多次运行,观察输出结果
"""
4.改变子线程退出方式
- 创建守候子线程
import time #导入线程模块 import threading def func1(): for i in range(100): print(f'func1运行中...{i+1}%') time.sleep(0.1) def func2(): for i in range(100): print(f'func2运行中...{i+1}%') time.sleep(0.1) if __name__ == '__main__': #1.创建线程对象 th1 = threading.Thread(target=func1,daemon=True) #主线程结束,子线程结束 th2 = threading.Thread(target=func2,daemon=True) #2.启动线程 th1.start() th2.start() print('主线程结束')
5.子线程间共享全局变量
import time
#导入线程模块
import threading
list = []
def func1():
for i in range(10):
list.append(i)
print(list)
time.sleep(0.2)
def func2():
for i in range(10):
list.append(i*10)
print(list)
time.sleep(0.2)
if __name__ == '__main__':
#1.创建线程对象
th1 = threading.Thread(target=func1)
th2 = threading.Thread(target=func2)
#2.启动线程
th1.start()
th2.start()
th1.join()
th2.join()
print(list)
print('主线程结束')
6.子线程共享全局变量导致的数据不安全
import time
#导入线程模块
import threading
a=0
def func1():
for i in range(1000000):
global a
a += 1
print('func1:',a)
def func2():
for i in range(1000000):
global a
a += 1
print('func2:',a)
if __name__ == '__main__':
#1.创建线程对象
th1 = threading.Thread(target=func1)
th2 = threading.Thread(target=func2)
#2.启动线程
th1.start()
th2.start()
th1.join()
th2.join()
print('main:',a)
print('主线程结束')
"""
当线程1读取全局变量,却还未进行修改时,发生CPU调度
此时线程2读取全局变量,进行修改
例如:
线程1读取到a=0,发生CPU调度
线程2读取到a=0,修改a=1,发生CPU调度
线程1修改a=1
但我们预期的结果是a在线程1中+1,在线程2中+1,结果为2
也就是说,这种情况下,a少加了一个1
"""
7.互斥锁
- 作用:对共享数据进行锁定,保证同一时刻只有一个线程去操作
- 互斥锁的使用流程
- 创建互斥锁
互斥锁对象 = threading.Lock()
- 使用互斥锁
#上锁 互斥锁对象.acquire() #解锁 互斥锁对象.release()
- 示例
import time #导入线程模块 import threading #实例化线程锁 mutex = threading.Lock() a=0 def func1(): for i in range(1000000): #上锁 mutex.acquire() global a a += 1 #解锁 mutex.release() print('func1:',a) def func2(): for i in range(1000000): #上锁 mutex.acquire() global a a += 1 # 解锁 mutex.release() print('func2:',a) if __name__ == '__main__': #1.创建线程对象 th1 = threading.Thread(target=func1) th2 = threading.Thread(target=func2) #2.启动线程 th1.start() th2.start() th1.join() th2.join() print('main:',a) print('主线程结束')
- 死锁:获得互斥锁的线程没有及时释放锁,导致其他线程无法获得锁
十一、其他Python语法
1.with语句
- with:用于简单高效地处理资源和异常
- with的使用
#使用with语句 #1.正常运行时 with open('1.txt','w') as fp: fp.write('hello world!') #2.出现异常时 with open('1.txt','r') as fp: fp.write('hello world!') """ 使用with出现异常时,尽管还是会报错 但已经打开的文件fp会被关闭,占用的资源会被回收 """
- 上下文管理器
- 定义:一个类只要实现了两个魔法方法
__enter__()
和__exit__()
,通过该类创建的对象就可以称之为上下文管理器 - with管理的对象是上下文管理器
- 自定义上下文管理器
class MyFile: def __init__(self,file_name,file_mode): self.file_name = file_name self.file_mode = file_mode self.file = None #重写__enter__ def __enter__(self): print('此处是上文') self.file = open(self.file_name,self.file_mode) return self.file #这个返回的是文件对象,不能调用类中的方法 #return self #这个返回值可以调用类中的方法,但在下面使用时要注意使用f.file.write() #重写__exit__ def __exit__(self,exc_type,exc_val,exc_tb):#这个参数一般是固定的 print('此处是下文') self.file.close() if __name__ == '__main__': with MyFile('1.txt','w') as f: f.write('hello world!!!')
- 定义:一个类只要实现了两个魔法方法
2.Python生成器
- 定义:Python生成器根据程序员指定的规则生成数据,条件不成立时生成结束
- 特点:生成器生成的数据是多次的,当数据被使用才会生成下一个,节省内存空间
- 创建生成器
- Python生成器并没有直接生成所有元素,其存储的只是生成数据的规则,当使用数据时才会生成
- 使用生成器推导式
#1.生成器推导式 my_generator1 = (i for i in range(10)) my_generator2 = (i*10 for i in range(10))
- 使用yield关键字
#2.yield def generator(num): for i in range(num): print('生成开始') yield i print('生成结束') my_generator2 = generator(10) print(next(my_generator2)) print(next(my_generator2)) print(next(my_generator2)) print(next(my_generator2)) """ 通过单步调试可以发现: 当程序运行到yield时,会产生阻塞,将i值返回 直到下一次迭代时,阻塞结束继续向下运行,直到再次遇到yield阻塞 """
- 遍历生成器
- 使用next单步迭代
#next:可以单步迭代生成器 next(my_generator2)
- 使用for循环
#1.通过for循环 #使用for循环时,无需进行异常处理 for i in my_generator1: print(i)
- 使用while循环
#2.通过while循环 #使用while循环时,需要进行异常处理 while True: try: print(next(my_generator2)) except: break
十二、正则表达式
1.概念
正则表达式描述了一种字符串的格式用于字符串的匹配
2.Python中使用正则表达式
- 导入模块
import re
- 使用正则
import re #1.使用match方法进行匹配操作 result1 = re.match('.e.','hello world!') """ match: 第一个参数是正则表达式 第二个参数是要进行匹配的原字符串 match会从左向右严格进行匹配 """ #2.使用search方法进行扫描操作 result2 = re.search('d.*','圆周率大约是3.14,因此我们可以粗略地求圆的面积') """ search: 第一个参数是正则表达式 第二个参数是要进行匹配的原字符串 """ #3.替换字符 sentence = '订购热线:xxx-xxx-xxxx!添加微信:WeChat_xxx,获取《20%折扣券》#限时优惠#' p = r'x|#|-|_' #要去除的字符的集合 r = re.compile(pattern=p) # str1 = r.sub('',sentence) #将第二个参数中符合条件的字符替换为第一个参数 print(str1) #4.replace方法 str2 = sentence.replace(':',':') #将sentence中符合第一个参数的字符替换为第二个参数 print(str2) #使用group方法进行提取数据 if result1: print(result1.group()) else: print('不存在这样的字符串') if result2: print(result2.group()) else: print('不存在这样的字符串')
3.正则匹配
- 单个字符匹配
匹配符 功能 . 匹配任意1个字符(n除外) [] 匹配[]中列举的字符 [^指定字符] 匹配除指定字符之外的字符 d 匹配数字 D 匹配非数字 s 匹配空白 S 匹配非空白 w 匹配普通字符(a-z,A-Z,0-9,_,汉字) W 匹配特殊字符 - 多个字符匹配
匹配符 功能 * 匹配前一个字符出现0次或无限次 + 匹配前一个字符出现1次或无限次 ? 匹配前一个字符出现1次或0次 {m} 匹配前一个字符出现m次 {m,n} 匹配前一个字符出现的次数属于区间[m,n] - 头尾匹配
匹配符 功能 ^ 匹配字符串开头 $ 匹配字符串结尾 - 匹配分组
匹配符 功能 | 或者 () 将括号内的字符作为一个分组 转义字符
4.分组引用
- 如果在正则表达式中分组了,那么使用group函数提取时,可以使用数字提取对应的组(0表示全部,从1开始对应自左向右的分组)
import re sentence = '订购热线:123-456-7890!添加微信:WeChat_xxx,获取《20%折扣券》#限时优惠#' result = re.match('w*:(d*-d*-d*)Ww*:(w*)',sentence) if result: print(result.group(0)) print(result.group(1)) print(result.group(2))
- 在正则表达式中可以通过
\数字
引用对应的分组import re sentence = '<html>www.xxx.com</html>' result = re.match('<([a-z]{4})>.*</\1>',sentence) #此处的‘\1'表示引用第一个分组 if result: print(result.group())
- 可以多次使用分组引用
import re sentence = '<html><h1>www.xxx.com</h1></html>' result = re.match('<([a-z]{4})><(wd)>.*</\2></\1>',sentence) if result: print(result.group())
- 可以为分组起别名以便调用,语法为:起名
(?P<name>)
调用(?P=name)
import re sentence = '<html><h1>www.xxx.com</h1></html>' result = re.match('<(?P<html>[a-z]{4})><(?P<h1>wd)>.*</(?P=h1)></(?P=html)>',sentence) if result: print(result.group())
十三、Python中的数据结构
1.定义
- 数据结构:是指存储时组织数据的方式,对于同样的数据采取不同的方式进行存储(例如用列表存储和用字典存储),就是采用了不同的数据结构
2.顺序表
- 一体式存储:存储的数据类型一致
- 分离式存储:由于存储的数据类型不一定相同,分离式存储会存储数据的首地址
3.链表
- 链表的存储不需要连续的存储空间
#结点的定义
class LinkListNode:
def __init__(self,item=None):
self.item = item #结点的数据域
self.next = None #结点的链接域
#单链表的定义
class LinkList:
def __init__(self,node=None):
self.head = node
#1.判空
def is_empty(self):
if self.head is None:
return True
else:
return False
#2.求长度
def length(self):
cur = self.head
length = 0
while cur is not None:
length += 1
cur = cur.next
return length
#3.遍历
def travel(self):
cur = self.head
while cur is not None:
print(cur.item,end=' ')
cur = cur.next
print('')
#4.头插
def add(self,item):
new_node = LinkListNode(item)
new_node.next = self.head
self.head = new_node
#5.尾插
def append(self,item):
new_node = LinkListNode(item)
if self.is_empty():
self.head = new_node
else:
cur = self.head
while cur.next is not None:
cur = cur.next
new_node.next = None
cur.next = new_node
#6.按下标插入
def insert(self,pos,item):
if pos <= 0:
self.add(item)
elif pos > self.length()+1:
self.append(item)
else:
new_node = LinkListNode(item)
cur = self.head
for i in range(pos-1):
cur = cur.next
new_node.next = cur.next
cur.next = new_node
#7.删除
def delete(self,item):
cur = self.head
pre = None
while cur is not None:
if cur.item == item:
if cur == self.head:
self.head = cur.next
else:
pre.next = cur.next
return
else:
pre = cur
cur = cur.next
#8.查找
def search(self,item):
cur = self.head
pos = 0
while cur is not None:
if cur.item == item:
return pos
else:
cur = cur.next
pos += 1
return -1
十四、Python中的排序算法
1.排序算法
- 算法:是指为了实现业务目标而采用的思路和方法,类似于数学题中的不同解法,不同的算法可能有不同的效率
- 稳定性:排序算法的稳定性是指相同的元素,在经过排序算法之后,其相对位置不发生改变
2.冒泡排序
def bubble_sort(list,len):
"""
:param list:传入要排序的列表
:param len: 传入列表的长度
:return:None
"""
#外层控制循环次数,总次数=列表长度-1
for i in range(len-1):
flag = 0
#内层控制遍历,遍历的长度是还未排序的列表长度-1
for j in range(len-i-1):
if list[j] > list [j+1]:
list[j],list[j+1] = list[j+1],list[j]
flag = 1
if flag == 0: #说明内部循环没有发生交换,列表已经有序,可以提前退出
break
#上面循环次数中都-1,是因为遍历到未排序列表的倒数第二个元素时,就已经和最后一个元素比较了
3.选择排序
def select_sort(list,len):
"""
:param list: 传入要排序的列表
:param len: 传入要排序列表的长度
:return: None
"""
#选择排序是找到元素的最终位置,将列表中的len-1个元素确定位置之后,最后一个元素的位置就自然确定了
for i in range(len-1):
min = i #假设第i个元素是最小的
for j in range(i+1,len): #因为已经假设第i位最小,所以不需要比较第i位和第i位
if list[j] < list[min]: #找到更小的则替换min
min = j
list[i],list[min] = list[min],list[i]
4.快速排序
def quick_sort(list,left,right):
"""
:param list:传入要排序的列表
:param left: 要排序子列表的左侧下标
:param right: 要排序子列表的右侧下标
:return: None
"""
#递归出口
if left >= right:
return
#记录分割元素
mid = list[left]
#定义左右指针
left_point = left
right_point = right
#当左指针在右指针左侧时,重复循环
while left_point < right_point:
#当右侧大于等于分割元素,使右指针向左移动
while list[right_point] >= mid and left_point < right_point:
right_point -= 1
#当右侧小于分割元素,将右指针的元素放入左指针的位置
list[left_point] = list[right_point]
#当左侧元素小于分割元素,使左指针向右移动
while list[left_point] <= mid and left_point < right_point:
left_point += 1
#当左侧大于分割元素,将左指针的元素放入右指针的位置
list[right_point] = list[left_point]
#此时左指针与右指针相遇,说明左指针指向的位置,左侧元素全部小于分割元素,右侧元素全部大于分割元素,将分割元素放入即可
list[left_point] = mid
#递归,将分割元素左右视为两个要排序的子列表
quick_sort(list,left,left_point-1)
quick_sort(list,left_point+1,right)