a, b = b, a + b
print() # 仅打印,无 语句
# 调用函数,无返回值(实际返回 None)
= fib(2000)
print() # 输出:None
3.闭包
闭包是这样一种情况: 处于嵌套函数当中, 有一个内部函数, 它引用了外部函数的局部变量, 除此之外存在关键表现即, 外部函数返回这个内部函数。
条件:
存在嵌套函数结构(内部函数定义在外部函数内)。
内部函数引用外部函数的局部变量。
外部函数返回内部函数对象。
def ():
def (x):
x * # 引用外部变量
# 返回内部函数
= (2)
print((5)) # 输出:10(闭包记住了 =2)
应用场景
1. 数据封装与状态保持
闭包能够将变量进行隐藏, 它是仅仅凭借返回的函数接口来实施操作行为的, 这样便能够实现简单的状态管理。
def ():
count = 0
def ():
count # 声明修改外部变量
count += 1
count
c = ()
print(c()) # 输出:1
将“print(c())”进行输出, 其结果为“2(处在被闭包保存状态下的count发生的显示呈现)”。
2. 函数工厂
根据参数动态生成具有不同行为的函数。
def (n):
def adder(x):
x + n
adder
add5 = (5)
print(add5(3)) # 输出:8
3. 装饰器
一项在某种环境里堪称极为强大的功能之一的事物是装饰器, 它具备这样的特性, 即在不针对原函数代码作出修改的情形之下, 可以基于动态方式来增强函数功能, 并且能够将开放封闭原则予以完美呈现。
1)基础原理
将原函数当作参数接收, 返回新函数的那种函数, 被称为是装饰器, 它属于高阶函数。那个新函数也就是闭包, 它把原函数调用以及额外的增强逻辑给封装起来了。
def (func):
def (*args, **):
print(f" 函数 {func.} 开始执行")
= func(*args, **) # 调用原函数
print(f" 函数 {func.} 执行完毕")
@ # 等价于 add = (add)
def add(a, b):
a + b
add(2, 3)
# 输出:
# 函数 add 开始执行
# 函数 add 执行完毕
2)进阶特性
1. 保留原函数元数据
运用.wraps装饰器, 能够留存被装饰函数的元数据, 像 、这样。
from wraps
def (func):
@wraps(func) # 复制原函数元数据到
def (*args, **):
# 增强逻辑
func(*args, **)
2. 带参数的装饰器
通过三层嵌套结构,允许装饰器接收参数,实现动态配置。
def ():
def (func):
@wraps(func)
def (*args, **):
=
@(=3)
def (name):
f"Hello, {name}!"
print(("Alice")) # 输出:
依次是, “你好, 爱丽丝!”, 其中这一句是“你好, 爱丽丝!”, 随后, 跟上仍旧这句“你好, 爱丽丝!”且句末置标点。
3)多个装饰器的执行顺序
存在多个装饰器, 当它们都作用于同一个函数的时候, 按照那种“从下到上、靠近函数的先去加以执行”的顺序来进行操作。
@
@ # 先执行
def demo():
pass
# 等价于 demo = ((demo))
实战案例
关于函数语法的掌握而言, 关键之处在于将理论与实践相结合, 以下借助典型案例, 进而巩固核心知识点, 同时解决实际开发之中的常见问题。
案例 1:参数传递错误排查
问题: 在调用函数之际, 出现提示“缺少位置参数”或者“意外的关键字参数”。分析: 参数传递的方式跟函数定义并不匹配, 像针对仅位置参数而言, 如果采用关键字进行传递, 或者参数顺序存在错误。解决: 核查函数定义的参数类型, 保证调用的方式能够契合位置/关键字的要求。
案例 2:此字段的默认参数陷阱
问题:默认参数为列表时,多次调用函数导致列表内容累积。
def (item, items=):
items.(item)
items
print((1)) # 输出:
print((2)) # 输出:(预期:)
缘由是, 默认参数于函数定义之际进行计算及缓存, 在多次调用时会共享同一个列表对象。要解决时, 需用None当作默认值, 并在函数内部对可变对象予以初始化。
def (item, items=None):
if items is None:
items =
items.(item)
items
案例 3:闭包实现带状态的函数
将需求明确表达为, 要去实现出一个计数器, 这个计数器每次被调用的时候, 都要进行自增操作, 而后返回当前的计数值, 并且不需要去使用全局变量。至于相应方案表述为, 借助闭包来保存计数状态。
def ():
count = 0
def ():
count
count += 1
count
= ()
print(()) # 1
print(()) # 2
= ()
print(()) # 1(独立计数器,不影响 )
案例 4:装饰器实现权限验证
要点在于, 针对多个函数, 要去添加权限检查, 而且是只有管理员才被允许去执行这些函数;在方案里用到的是, 利用装饰器来统一达成权限验证这一逻辑。
from wraps
def (func):
@wraps(func)
def (user, *args, **):
要是用户获取到的“role”的值不等于“admin”。
raise ("仅管理员可执行此操作")
func(user, *args, **)
@
def (user, ):
print(f"删除资源 {}")
admin = {"role": "admin"}
(admin, 123) # 正常执行
guest = {"role": "guest"}
(guest, "abc") # 抛出
案例 5:函数参数的类型注解与检查
需求: 给函数增添参数类型提示, 并且于运行阶段查证参数类型是否准确。方案: 运用3.5 以上的类型注解, 并搭配模块以及装饰器达成类型检查。
from wraps
from , Union
T = ('T')
def (func):
@wraps(func)
def (*args, **):
# 获取函数参数的类型注解
= func.
# 检查位置参数类型
针对i, 对于(arg以及name)而言 通过(经由zip(args以及func..))达成。
if name in and not (arg, ):
引发(格式化("参数 {名称} 应当是 {} 类型, 实际却是 {类型(参数)}"))。
func(*args, **)
@
定义一个名叫add整型函数, 它接收两个整型参数a和b, 返回值为整型。
a + b
add(2, 3) # 正常执行
把“2”添加进去时, 3这个值, 会出现抛出的情况, 即参数a应该是某种类型, 然而实际呈现的却是。
案例 6:高阶函数实现数据处理流水线
需求: 针对列表当中的数据, 依照顺序逐个应用多个不同的处理函数, 像过滤掉某些元素、将元素进行转换、按照特定规则排序等操作, 从而构建成一条处理的流水线。方案: 去定义具有更高阶性质的函数, 该函数接收数据以及处理函数所组成的列表, 之后按照给定的顺序依次去执行这些处理。
def (data, ):
for func in :
data = func(data)
data
# 定义处理函数
def ():
n for n in if n % 2 == 0
def ():
n **2 for n in
def ():
()
# 构建流水线
=
, ,
data =
1, 2, 3, 4, 5, 6
= (data, )
print() # 输出:
4, 16, 36
(过滤偶数→平方→升序排序)