目录
一、闭包和装饰器
1. 闭包
定义
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
构成条件
在函数嵌套(函数里面再定义函数)的前提下
内部函数使用了外部函数的变量(还包括外部函数的参数)
外部函数返回了内部函数
作用 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。
注意点:
由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。
格式
# 外部函数
def test1(a):
b = 10
# 内部函数
def test2():
# 内部函数使用了外部函数的变量或者参数
print(a, b)
# 返回内部函数, 这里返回的内部函数就是闭包实例
return test2
实现
# ATM案例 外部函数是ATM设备,投放一台设备需要放入一些现金.
def ATM_Device(ATM_money): # todo 函数的参数也是函数的局部变量
# 局部变量
# ATM_money = 10000
# 局部函数
def ATM_operation(type, money): # 函数嵌套
nonlocal ATM_money # todo nonlocal的作用是 声明内部函数使用外部函数的局部变量.
if type == 1:
ATM_money += money # todo 内部函数使用了外部函数的变量
elif type == 0:
ATM_money -= money
else:
print("您的操作有误")
print(f"ATM当前的余额为:{ATM_money}")
return ATM_operation # todo 外部函数返回内部函数注意不要加括号,加括号表示调用,不加括号表示函数本身.
if __name__ == '__main__':
# ATM_operation() 我们想操作内部函数,但是不能直接调用.(调用外部函数返回值就是内部函数)
ATM_oper = ATM_Device(10000) # 相当于创建一个变量来'接收'内部函数的功能.
# 相当于调用外部函数,并且接收它的返回值,等同于内部函数完全复制给这个变量.
ATM_oper(1, 2000) # ATM当前的余额为:12000 # 相当于调用内部函数
ATM_oper(0, 1000) # ATM当前的余额为:11000
ATM_oper(0, 5000) # ATM当前的余额为:6000 可以看到这个变量一直存在.
2. 装饰器
# 定义 给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰
# 功能特点
不修改已有函数的源代码
不修改已有函数的调用方式
给已有函数增加额外的功能
# 使用场景 函数执行时间的统计
输出日志信息
# 注意点 被装饰的函数与内部函数(格式等)需一致
# 格式
# 装饰器
# def decorator(fn): # fn:被装饰的目标函数.
# def inner():
# '''执行函数之前'''
# fn() # 执行被装饰的目标函数
# '''执行函数之后'''
# return inner
# 实现
def outer(fn): # fn = sleep
def inner():
print("睡前泡脚睡更香")
fn() # 相当于 sleep.
return inner
@outer # 使用装饰器:被装饰的函数会当做参数传递给装饰器. 这时候 fn 相当于 sleep.
# 可以将 @ 当做装饰器的意思 @谁相当于装饰谁 下面的函数当做参数传给@后面的函数.
def sleep():
print("每天保证7小时睡眠")
if __name__ == '__main__':
sleep() # 调用sleep相当于装饰 outer 这个函数,然后执行outer中的 inner闭包函数 执行完 inner之后再执行fn(sleep).
# 通用装饰器/带参数的装饰器
# 格式
# 通用装饰器
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力计算--")
result = fn(*args, **kwargs)
return result
return inner
# 实现
def logging(fn):
def inner(*args):
print("努力计算中...")
result = fn(*args)
return result
return inner
@logging
def num_plus(a,b,c):
return a+b+c
if __name__ == '__main__':
print(num_plus(1,2,3))
二、多任务编程
1. 概述
并发 交替执行
并行 同时执行
2. 进程
定义
一个正在运行的程序或者软件就是一个进程,它是操作系统进行资源分配的基本单位
一个程序运行后至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程。
多进程
Process进程类的说明
Process([group [, target [, name [, args [, kwargs]]]]])
group:指定进程组,目前只能使用None
target:执行的目标任务名
name:进程名字
args:以元组方式给执行任务传参
kwargs:以字典方式给执行任务传参|
Process创建的实例对象的常用方法:
start():启动子进程实例(创建子进程)
join():等待子进程执行结束
terminate():不管任务是否完成,立即终止子进程
Process创建的实例对象的常用属性:
name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
实现基本步骤
导入进程包
import multiprocessing
创建子进程并指定执行的任务
sub_process = multiprocessing.Process (target=任务名)
启动进程执行任务
sub_process.start()
import multiprocessing
import time
def sing():
print(multiprocessing.current_process().name) # 获取当前进程的名字 我的 1号进程.
for i in range(3):
print("唱")
time.sleep(1)
def dance():
print(multiprocessing.current_process().name) # 我的 2号进程
for i in range(3):
print("跳")
time.sleep(1)
if __name__ == '__main__':
print(multiprocessing.current_process().name) # MainProcess
# 创建一个进程
P1 = multiprocessing.Process(target=sing,name="我的1号进程")
P1.start() # 开启子进程 1
P2 = multiprocessing.Process(target=dance,name="我的2号进程")
P2.start() # 开启子进程 2
获取当前进程编号
print(f"sing:{os.getpid()}") # 获取进程编号
print(f"sing:{os.getppid()}") # 获取主进程编号
os.kill(os.getpid(), 9) # 杀死进程
执行带参数的任务
# args: 以元组的方式给任务传入参数
sub_process = multiprocessing.Process(target=task, args=(5,))
# kwargs: 表示以字典方式传入参数
sub_process = multiprocessing.Process(target=task, kwargs={"count": 3})
import multiprocessing
import time
def task(count,a):
print(a)
for i in range(count):
print(f"任务执行中...{i}")
time.sleep(0.2)
if __name__ == '__main__':
# P1 = multiprocessing.Process(target=task,args=(5,)) # 传一个参数为元组
# P1 = multiprocessing.Process(target=task, args=(5,20)) # 传两个参数为元组
# P1.start()1
# P2 = multiprocessing.Process(target=task,kwargs={"count":5}) # 传一个参数为字典
P2 = multiprocessing.Process(target=task, kwargs={"count": 5, "a": 20 }) # 传两个参数为字典
P2.start()
注意点 为了保证子进程能够正常的运行,主进程会等所有的子进程执行完成以后再销毁
import multiprocessing
import time
# 定义进程所需要执行的任务
def task():
for i in range(10):
print("任务执行中...")
time.sleep(0.2)
if __name__ == '__main__':
# 创建子进程
sub_process = multiprocessing.Process(target=task)
sub_process.start()
# 主进程延时0.5秒钟
time.sleep(0.5)
print("over")
exit()
设置守护线程可以在主线程结束后立刻挂掉.
# 设置守护主进程方式: 子进程对象.daemon = True
# 销毁子进程方式: 子进程对象.terminate()
import multiprocessing
import time
def task():
for i in range(10):
print(f"任务执行中...{i}")
time.sleep(0.2)
if __name__ == '__main__':
# P1 = multiprocessing.Process(target=task,daemon=True) # daemon = True 表示制造守护进程.
P1 = multiprocessing.Process(target=task) # 守护进程指的是主进程退出子进程销毁不再执行.
P1.daemon = True # 后期制造守护进程
P1.start()
time.sleep(0.5)
print("main:over")
# P1.terminate() # 杀死子进程
exit() # 退出程序
3. 线程
定义
线程是进程中执行代码的一个分支,每个执行分支(线程)要想工作执行代码需要cpu进行调度 ,也就是说线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程。
多线程
线程类Thread参数说明 Thread([group [, target [, name [, args [, kwargs]]]]])
group: 线程组,目前只能使用None
target: 执行的目标任务名
args: 以元组的方式给执行任务传参
kwargs: 以字典方式给执行任务传参
name: 线程名,一般不用设置
实现基本步骤
导入线程模块
import threading
创建子线程并指定执行的任务
sub_thread = threading.Thread(target=任务名)
启动线程执行任务
sub_thread.start()
执行带有参数的任务
同进程
注意点
线程之间执行是无序的
线程之间执行是无序的,它是由cpu调度决定的 ,cpu调度哪个线程,哪个线程就先执行,没有调度的线程不能执行。
进程之间执行也是无序的,它是由操作系统调度决定的,操作系统调度哪个进程,哪个进程就先执行,没有调度的进程不能执行
主线程会等待所有的子线程执行结束再结束
同进程线程之间共享全局变量
import time
import threading
g_num = 0
def sum_num1():
for i in range(10000):
global g_num
g_num += 1
print(f"sum1:",g_num)
def sum_num2():
for i in range(10000):
global g_num
g_num += 1
print(f"sum2:",g_num)
if __name__ == '__main__':
T3 = threading.Thread(target=sum_num1)
T3.start()
T3.join()
T4 = threading.Thread(target=sum_num2)
T4.start()
time.sleep(3)
print(f"main:{g_num}")
线程之间共享全局变量数据出现错误问题
共享出错解决方案
线程等待 : join()
同步锁: Lock()
4. 进程和线程的区别
进程和线程的区别
关系对比
线程是依附在进程里面的,没有进程就没有线程。
一个进程默认提供一条线程,进程可以创建多个线程。
区别对比
进程之间不共享全局变量
线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步
创建进程的资源开销要比创建线程的资源开销要大
进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
线程不能够独立执行,必须依存在进程中
多进程开发比单进程多线程开发稳定性要强
优缺点对比
进程优缺点:
优点:可以用多核
缺点:资源开销大
线程优缺点:
优点:资源开销小
缺点:不能使用多核
三、With语句和正则表达式
1. With语句
With语句
实现
with open ("Alfie.txt","w",encoding="utf8") as f:
# 这里使用 f 就是在使用文件通道,优点是不用关闭通道.
f.write("Hello\nWorld")
作用: 使用with语句打开文件,可以自动关闭资源.
2. 深浅拷贝
浅拷贝
格式 copy.copy()
作用
只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象.
# 若对源数据进行修改,拷贝的数据也会被同步修改.
可变类型 list,set,dict
可变类型进行浅拷贝只对可变类型的第一层对象进行拷贝,对拷贝的对象会开辟新的内存空间进行存储.
# 子对象不进行拷贝。
list1 = [1,2,3,[4,5]]
list2 = copy.copy(list1)
print(list2)
list1[3][0] = 400 # 修改源数据
print(list2) # 打印修改后的数据 # [1, 2, 3, [400, 5]]
print(f"list1:{id(list1)} vs list2:{id(list2)}") # list1:2242964762176 vs list2:2242964682112
不可变类型 int,float,str,bool,tuple
不可变类型进行浅拷贝不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用.
str1 = "Alfie"
# str2 = copy.copy(str1)
str2 = str1 # 与上述方式是一样的效果
print(str2)
print(f"str1:{id(str1)} vs str2:{id(str2)}") # str1:2049586960112 vs str2:2049586960112
tuple1 = (1,2,3,[4,5])
# tuple2 = copy.copy(tuple1)
tuple2 = tuple1 # 与上述方式是一样的效果
print(tuple2)
print(f"tuple1:{id(tuple1)} vs tuple2:{id(tuple2)}") # tuple1:2134440411920 vs tuple2:2134440411920
深拷贝
格式 copy.deepcopy()
作用
只要发现对象有可变类型就会对该对象到最后一个可变类型的每一层对象就行拷贝
# 对每一层拷贝的对象都会开辟新的内存空间进行存储。
可变类型
深拷贝特点:每一层都开辟空间.
list1 = [1,2,3,4]
list2 = copy.deepcopy(list1)
print(list2)
print(f"list1:{id(list1)} vs list2:{id(list2)}") # list1:2462320373056 vs list2:2462320373312
不可变类型
有可变类型就会对该对象到最后一个可变类型的每一层对象就行拷贝
tuple1 = (1,2)
tuple2 = copy.deepcopy(tuple1)
print(tuple2)
print(f"tuple1:{id(tuple1)} vs tuple2:{id(tuple2)}") # tuple1:2502340507712 vs tuple2:2502340507712
tuple3 = (1,2,[3,4])
tuple4 = copy.deepcopy(tuple3)
print(f"tuple3:{id(tuple3)} vs tuple4:{id(tuple4)}") # tuple3:1685285692608 vs tuple4:1685286836864
print(f"tuple3:{id(tuple3[2])} vs tuple4:{id(tuple4[2])}") # tuple3:1685286759936 vs tuple4:1685286680128
3. 正则表达式
概念 正则表达式就是记录文本规则的代码
作用 则表达式是匹配符合某些规则的字符串数据
python中正则模块
Python中需要通过正则表达式对字符串进行匹配的时候,可以使用一个 re 模块
re.match() 根据正则表达式从头开始匹配字符串数据
正则的单个字符规则
代码 功能
. 匹配任意1个字符(除了\n)
[ ] 匹配[ ]中列举的字符
\d 匹配数字,即0-9
\D 匹配非数字,即不是数字
\s 匹配空白,即 空格,tab键
\S 匹配非空白
\w 匹配非特殊字符,即a-z、A-Z、0-9、_、汉字
\W 匹配特殊字符,即非字母、非数字、非汉字
正则的多个字符规则
代码 功能
* 匹配前一个字符出现0次或者无限次,即可有可无
+ 匹配前一个字符出现1次或者无限次,即至少有1次
? 匹配前一个字符出现1次或者0次,即要么有1次,要么没有
{m} 匹配前一个字符出现m次
{m,} 匹配前一个字符至少出现m次
{m,n} 匹配前一个字符出现从m到n次
匹配开头和结尾
代码 功能
^ 匹配字符 串开头
$ 匹配字符串结尾
[^] ^出现在[]中表示取反
匹配分组
代码 功能
| 匹配左右任意一个表达式
(ab) 将括号中字符作为一个分组
\num 引用分组num匹配到的字符串
(?P<name>) 分组起别名
(?P=name) 引用别名为name分组匹配到的字符串
正则扩展
# 查询匹配方式.
# 扩展: re.match() , re.search() , re.findall()
import re
my_str = "我读过的书有<<三国演义>>和<<红楼梦>>我最喜欢的书是<<论语>>另外<<易经>>也准备研究"
regex = "<<.+?>>"
# 正则模式是贪婪匹配模式..
# 如何改变匹配模式.使用?改变,?写在数量词的右边.
# todo match从左边匹配,匹配不到就结束.
print(re.match(regex, my_str)) # None match()从左边匹配,匹配不到就结束.
# todo search,只能匹配一个元素.匹配到就停止如果匹配不到就会一直往下走到底..
print(re.search(regex, my_str)) # <re.Match object; span=(6, 14), match='<<三国演义>>'>
# todo findall,可以匹配多个元素.直到要匹配的字符串结束.
print(re.findall(regex, my_str)) # ['<<三国演义>>', '<<红楼梦>>', '<<论语>>', '<<易经>>']
总结
以上就是今天的内容,本文介绍了Python中闭包和装饰器的原理及使用,多任务编程的概念和应用,With语句和正则表达式的相关知识。
这篇博客详细介绍了Python的闭包和装饰器概念,多任务编程中的进程与线程的区别,以及With语句和正则表达式的基础知识。内容包括并发与并行的定义,进程线程的关系和优缺点,并深入讲解了With语句的作用。
2万+

被折叠的 条评论
为什么被折叠?



