有参装饰器、迭代器、生成器和协程函数
Python装饰器、迭代器与生成器完整进阶笔记
一、Python装饰器
1.1 装饰器核心基础概念
装饰器本质是一个闭包函数,作用是在不修改原函数代码、不改变原函数调用方式的前提下,为函数新增额外功能,符合编程中的开闭原则:对扩展开放,对修改关闭。
装饰器分为无参装饰器和有参装饰器两类,无参装饰器仅能实现固定功能扩展,有参装饰器可通过传入参数实现动态定制化功能,适配更多业务场景。
1.2 无参装饰器
1.2.1 无参装饰器通用模板
无参装饰器结构固定,外层函数接收原函数作为参数,内层wrapper函数负责执行原函数、扩展新增逻辑,最终返回内层函数地址。
def deco(func):
def wrapper(*args,**kwargs):
# 函数执行前扩展逻辑
res = func(*args,**kwargs)
# 函数执行后扩展逻辑
return res
return wrapper
# 装饰器使用
@deco
def index():
pass
1.2.2 多装饰器叠加执行逻辑
当多个装饰器同时装饰一个函数时,存在两个核心执行阶段,顺序完全不同,是装饰器的核心考点:
加载阶段(外层装饰器函数执行):自下而上执行,优先执行靠近原函数的装饰器外层逻辑
执行阶段(内层wrapper函数执行):自上而下执行,优先执行最外层装饰器的内层逻辑
加载阶段仅在代码加载时执行一次,执行阶段在调用原函数时触发执行。
def deco1(func1):
print("deco1...")
def wrapper1(*args, **kwargs):
print('wrapper1...')
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def deco2(func2):
print("deco2...")
def wrapper2(*args, **kwargs):
print('wrapper2...')
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def deco3(func3):
print("deco3...")
def wrapper3(*args, **kwargs):
print('wrapper3...')
res3 = func3(*args, **kwargs)
return res3
return wrapper3
# 多装饰器叠加装饰
@deco1
@deco2
@deco3
def index():
print('from index')
return 123
# 调用函数触发执行阶段
res = index()
print(res)
对应输出结果:
加载阶段输出:deco3...、deco2...、deco1...
执行阶段输出:wrapper1...、wrapper2...、wrapper3...、from index、123
1.2.3 无参装饰器实战案例(固定逻辑权限校验)
实现固定文件模式的账号密码登录校验,为目标函数新增登录鉴权功能。
def auth(func):
def wrapper(*args, **kwargs):
username = input("username>>>: ").strip()
password = input("password>>>: ").strip()
if username == "egon" and password == "123":
print("登录成功")
res = func(*args, **kwargs)
return res
else:
print("账号或密码错误")
return wrapper
@auth
def index():
print('from index ....')
index()
1.3 有参装饰器
1.3.1 有参装饰器通用模板
有参装饰器在无参装饰器基础上,外层嵌套一层函数用于接收自定义参数,实现装饰器逻辑动态化,参数可自定义传递,适配不同场景需求。
def outter(xxx):
def deco(func):
def wrapper(*args,**kwargs):
# 可使用外层传递的参数xxx
print(xxx)
res = func(*args,**kwargs)
return res
return wrapper
return deco
# 有参装饰器使用
@outter(111)
def index():
pass
1.3.2 有参装饰器实战案例(动态数据源权限校验)
通过传入参数动态指定校验数据源,支持file、mysql、ldap多种校验模式,实现装饰器功能复用。
def outter(engine,xxx):
def auth(func):
def wrapper(*args, **kwargs):
print(xxx)
username = input("username>>>: ").strip()
password = input("password>>>: ").strip()
if engine == "file":
print("账号密码存放在文件中")
if username == "egon" and password == "123":
print("登录成功")
res = func(*args, **kwargs)
return res
else:
print("账号或密码错误")
elif engine == "mysql":
print("账号密码存放在数据库中")
elif engine == "ldap":
print("账号密码存放在ldap")
else:
print("未知engine")
return wrapper
return auth
# 传入参数指定mysql数据源模式
@outter('mysql',111)
def index():
print('from index ....')
index()
二、迭代器
2.1 迭代器核心概念
迭代:是重复执行的过程,且每一次重复操作都基于上一次的结果,区别于单纯的无逻辑重复。
迭代器:是Python中通用的迭代取值工具,核心作用是提供不依赖索引的通用取值方案,适配所有可迭代数据类型。
2.1.1 迭代与普通重复的区别
单纯重复(非迭代):无状态继承,每次执行相互独立
i = 0
while i < 5:
print(i)
真正迭代:基于上一次结果更新状态,逐步推进
i = 0
names = ["egon","lili",'jack']
while i < len(names):
print(i,names[i])
i += 1
2.2 迭代器的存在意义
Python中字符串、列表、元组、字典、集合、文件对象,均需要迭代取值。传统索引取值方式仅支持有序序列类型(字符串、列表、元组),无法适配字典、集合等无序类型。迭代器屏蔽数据类型差异,提供统一、通用的迭代取值方案。
2.3 可迭代对象与迭代器对象
2.3.1 可迭代对象(iterable)
定义:内置有__iter__()方法的对象,统称为可迭代对象。
常见可迭代对象:str、list、tuple、dict、set、文件对象。
特性:可迭代对象调用__iter__()方法,可转换为迭代器对象。
2.3.2 迭代器对象(iterator)
定义:同时内置__iter__()和__next__()方法的对象。
核心方法:
next():取出迭代器中的下一个值,取值完毕后继续调用会抛出StopIteration异常
iter():返回迭代器自身,无实际迭代逻辑变更
核心关系:迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象。文件对象是原生迭代器对象。
2.3.3 迭代器取值示例
# 字典迭代取值
dic = {"a": 1, 'b': 222, 'c': 333}
dic_iterator = dic.__iter__()
# 单次取值
print(dic_iterator.__next__())
print(dic_iterator.__next__())
print(dic_iterator.__next__())
# 循环捕获异常取值(通用方案)
dic_iterator = dic.__iter__()
while True:
try:
print(dic_iterator.__next__())
except StopIteration:
break
# 迭代器一次性特性:取值完毕后无法再次取值
print("迭代器取值完毕,二次迭代无效")
for k in dic_iterator:
print(k)
2.4 for循环底层工作原理
for循环本质是迭代器循环,底层完全基于迭代器实现,自动完成迭代、取值、异常捕获,无需手动处理StopIteration异常。
for循环执行流程:
1、调用in后可迭代对象的__iter__()方法,生成迭代器对象;
2、循环调用迭代器的__next__()方法,取值并赋值给循环变量,执行循环体;
3、捕获StopIteration异常,自动终止循环。
# for循环迭代字典
dic = {"a": 1, 'b': 222, 'c': 333}
for k in dic:
print(k)
# 文件对象迭代(原生迭代器)
with open('a.txt',mode='rt',encoding='utf-8') as f:
for line in f:
print(line.strip())
# 迭代器取值完毕,二次循环无输出
print("二次读取文件内容:")
for line in f:
print(line.strip())
2.5 迭代器的优缺点
2.5.1 优点
1、通用性强:统一有序、无序数据类型的迭代取值方案,是for循环实现的底层基础;
2、节省内存:惰性计算,同一时刻内存中仅保留一个迭代值,可处理无限数据流,区别于列表、字典等全量加载数据的容器类型。
2.5.2 缺点
1、无法预知长度:迭代器未取值完毕前,无法获取总数据量;
2、一次性取值:迭代器状态单向推进,取值完毕后彻底失效,无法回溯、重复迭代,多循环共用同一个迭代器时,仅第一个循环可取值。
三、生成器
3.1 生成器核心概念
生成器本质是自定义的迭代器,是开发者通过代码手动创建的迭代器,拥有原生迭代器所有特性,同时支持自定义迭代逻辑,灵活性远超原生迭代器。
3.2 生成器的存在意义
原生迭代器仅支持系统内置数据类型迭代,逻辑固定无法修改。生成器允许开发者自定义迭代规则,结合迭代器惰性计算、节省内存的核心优势,可按需生成无限数据流,适配大数据批量处理、流式数据读取等场景。
3.3 生成器创建与执行原理
创建规则:函数内部只要包含yield关键字,该函数就不再是普通函数,调用函数不会执行函数体,而是直接返回一个生成器对象(自定义迭代器)。
执行规则:通过__next__()触发生成器执行,代码运行至yield处暂停,返回yield后的值;再次触发__next__(),从暂停位置继续执行,直至无yield关键字,抛出StopIteration异常。
3.3.1 基础生成器示例
def func():
print("111111")
yield 1
print("222222")
yield 2
print("333333")
yield 3
print("4444444")
# 生成生成器对象,不执行函数体
g = func()
# 触发执行,分段取值
print(g.__next__())
print(g.__next__())
print(g.__next__())
# 取值完毕,触发异常
# print(g.__next__())
3.4 自定义range生成器案例
基于生成器原理,复刻Python内置range函数的迭代逻辑,实现自定义数值迭代器。
def my_range(start, stop, step=1):
print("start...")
while start < stop:
yield start
start += step
print("stop...")
# 创建生成器
g = my_range(1, 5, 2)
# for循环迭代取值
for item in g:
print(item)
# 生成器取值完毕,二次迭代无输出
print("======")
for item in g:
print(item)
3.5 yield与return的核心区别
相同点:均可返回任意类型、任意数量的数据。
不同点:
1、return:一个函数仅支持一个return,执行return后函数立即终止,仅能返回一次值;
2、yield:一个函数支持多个yield,执行yield后函数暂停、保存运行状态,可多次返回值,实现分段迭代。
3.6 生成器send方法(传值交互)
send()是生成器专属方法,不仅可以触发生成器继续执行,还可以向暂停的yield位置传递参数,实现生成器内外数据交互,是生成器高级用法。
使用规则:首次调用必须传send(None),初始化生成器至yield暂停状态,后续可传递任意参数。
def eater(name):
print("%s 准备开始吃饭啦" % name)
food_list = []
while True:
food = yield food_list
print("%s 吃了 %s" % (name, food))
food_list.append(food)
g = eater("egon")
# 初始化生成器
res = g.send(None)
print(res)
# 向生成器传值
res = g.send("蒸羊羔")
print(res)
res = g.send("蒸鹿茸")
print(res)
# 关闭生成器,后续无法传值执行
# g.close()
# g.send("蒸熊掌")

浙公网安备 33010602011771号