目录
1.闭包
1.闭包定义
闭包(Closure):当一个内部函数引用了外部函数的变量时,即使外部函数已执行完毕,这些变量仍会被保留在内存中,形成闭包。
三要素
- 嵌套函数(内部函数定义在外部函数内)
- 内部函数引用外部函数的变量
- 外部函数返回内部函数
def out(exp):
def inner(base):
return base**exp #内部函数引用外部变量
return inner # 返回内部函数对象(非调用结果)
# 1. 调用外部函数,exp=2
# 2. 返回inner函数对象(绑定exp=2)
# 3. square现在指向这个绑定函数
square=out(2)
# 查看square对象
square
<function out.<locals>.inner at 0x000001F668E8CB80>
# 使用闭包函数( 实际调用inner(5) )
square(5)
25
cube=out(3)
cube
<function out.<locals>.inner at 0x000001F668E8CC10>
cube(5)
125
当调用square(5)
时:
- Python检查
inner
函数的作用域,在局部作用域找不到exp
- 查找闭包环境(
__closure__
),找到绑定的exp=2
- 完成计算
5**2
函数地址不同
-
square
地址:0x000001F668E8CB80
cube
地址:0x000001F668E8CC10
- 每次调用
out()
都会创建一个新的inner
函数对象
闭包环境独立
-
square
捕获exp=2
cube
捕获exp=3
- 每个闭包都有自己的变量环境
2.闭包的__closure__属性
闭包比普通的函数多了一个__closure__ 属性,该属性记录着自由变量的地址。当闭包被调用时,系统就会根据该地址找到对应的自由变量,完成整体的函数调用。
闭包原理:当外部函数执行时,Python 会为内部函数创建一个闭包环境,将引用的外部变量(也称自由变量)保存在内部函数的 __closure__ 属性中。即使外部函数执行完毕,这些变量依然存在。
__closure__
是什么
- 一个包含"cell"对象的元组(或None,如果不是闭包)
- 每个cell对象对应闭包捕获的一个外部变量
- 可通过
cell.cell_contents
访问变量的当前值
def outer(exp):
def inner(base):
return base ** exp
return inner
square = outer(2)
#输出一个cell对象,类型是一个元组(说明闭包可以支持多个自由变量的形式)
>>> square.__closure__
(<cell at 0x000001C2CBEF0A68: int object at 0x00007FFD26B27120>,)
>>> type(square.__closure__)
<class 'tuple'>
# 通过cell_contents获取捕获的变量值
>>> square.__closure__[0].cell_contents # 输出: 2 (即exp的值)
2
#查看自由变量名
>>> square.__code__.co_freevars
('exp',)
多变量
def counter(start=0, step=1):
count = start # 初始化计数器值
def increment():
nonlocal count # 声明count来自外部作用域
count += step # 修改外部变量
return count # 返回更新后的值
return increment # 返回内部函数
# 创建计数器实例
c1 = counter(10, 2) # start=10, step=2
>>> c1()
12
>>> c1()
14
>>> c1.__closure__
(<cell at 0x000001C2CBEF0F18: int object at 0x00007FFD26B27220>, <cell at 0x000001C2CBEF0DC8: int object at 0x00007FFD26B27120>)
>>> len(c1.__closure__)
2
>>> c1.__closure__[0].cell_contents
10
>>> c1.__closure__[1].cell_contents
2
解析:
1.创建计数器 c1 = counter(10, 2)
调用 counter(10, 2)
函数;
在 counter
的作用域中:
-
start = 10
step = 2
count = start = 10
定义内部函数 increment
返回 increment
函数对象(此时闭包形成)
2.第一次调用c1()
执行 increment()
:
nonlocal count # 访问闭包中的count变量
count = count + step # 10 + 2 = 12
return count # 返回12
闭包环境更新
3.第二次调用c1()
再次执行increment()
:
nonlocal count # 访问闭包中的count变量(现在是12)
count = count + step # 12 + 2 = 14
return count # 返回14
闭包环境再次更新
2.装饰器
1.装饰器定义
在不修改原来代码情况下增加新功能,拿函数当参数。
装饰器(Decorator) 是一种高阶函数,用于动态修改或增强其他函数或类的行为,而无需修改其源代码。它是基于闭包(Closure)实现的语法糖,广泛应用于日志记录、权限校验、性能测试、缓存等场景。
核心特点:
装饰器的本质是接受一个函数作为参数,并返回一个新函数的闭包。其核心步骤如下:
定义一个装饰器函数:接收被装饰的函数作为参数。
定义包装函数:在装饰器内部定义一个包装函数(如 wrapper),用于扩展原函数的功能。
返回包装函数:将包装函数作为装饰器的返回值,替代原函数。
@语法糖:@decorator 等价于 原函数 = decorator(原函数)。
2.无参装饰器
def decorator(func): #装饰器函数,接收被装饰的函数
def wrapper(*args,**kargs): #包装函数,接收任意参数
# 增强功能代码
print("我可以装饰任何函数")
ret=func(*args,**kargs) #调用原函数
return ret
return wrapper #返回包装函数
@decorator #语法糖,相当于my_func=decorator(my_func)
def my_func(name):
print(f"hello,{name}!")
my_func("张三")
输出:
我可以装饰任何函数
hello,张三!
@decorator
def my_func(name):
print(f"hello,{name}!")
my_func("张三")
等价于
def my_func(name):
print(f"hello,{name}!")
#将原函数my_func传递给装饰器decorator,装饰器返回包装后的wrapper函数,
并重新赋值给my_func
my_func=decorator(my_func)
my_func("张三") #实际上是调用wrapper(“张三”)
装饰器本质上就是闭包的应用;
当外部函数(装饰器函数decorator)执行时,python为内部函数(包装函数wrapper)创建一个闭包环境,将引用的外部变量(函数my_func)保存在内部函数(包装函数wrapper)的__closure__属性中。
print(my_func.__closure__)
(<cell at 0x000001F668E431F0: function object at 0x000001F668E8CF70>,)
print(my_func.__closure__[0].cell_contents)
<function my_func at 0x000001F668E8CF70>
3.带参装饰器
在外层再嵌套一层函数,来传递参数。
def repeat(n): # 外层函数接收参数 n
def decorator(func): # 中层函数接收被装饰的函数 func
def wrapper(*args,**kargs): # 内层包装函数
print("我可以装饰任何函数")
for i in range(n):
ret=func(*args,**kargs)
return ret
return wrapper # 返回包装后的函数
return decorator # 返回装饰器函数
def my_func(name):
print(f"hello,{name}!")
my_func=repeat(3)(my_func) #等价于@repeat(3)
my_func("张三")
输出:
我可以装饰任何函数
hello,张三!
hello,张三!
hello,张三!
#my_func=repeat(3)(my_func):
步骤分解:
repeat(3):调用外层函数 repeat,传入参数 n=3,返回中层函数 decorator。
decorator(my_func):调用中层函数 decorator,传入原函数 my_func,返回内层函数 wrapper。
重新赋值:将 my_func 变量指向 wrapper 函数,完成函数替换。
repeat(3):生成一个固定 n=3 的装饰器 decorator,n=3保存在 decorator 的闭包中。
decorator(my_func): 将 my_func 替换为 wrapper,wrapper 闭包中保存了 func=my_func 和 n=3。
闭包变量:n 和原函数 my_func 通过闭包被 wrapper 记住,即使外层函数已执行完毕。
调用 my_func("张三"): 实际执行 wrapper("张三"),触发循环调用原函数 3 次。
总结:
装饰器本质:通过闭包和函数替换,动态增强函数功能。
带参装饰器:需三层嵌套函数,外层接收参数,中层接收函数,内层实现增强逻辑。
执行逻辑:调用装饰后的函数时,实际触发包装函数逐层穿透闭包,执行核心逻辑。
4.多个装饰器用在同个函数
装饰器链:多个装饰器按从下到上的顺序应用。
def add(func):
def inner():
x = func()
return x + 1
return inner
def cube(func):
def inner():
x = func()
return x * x * x
return inner
def square(func):
def inner():
x = func()
return x * x
return inner
#等价于test = add(cube(square(test)))
@add
@cube
@square
def test():
return 2
print(test()) #输出65
第一层装饰@square执行流程:
定义 test → 返回 2
↓
应用 @square → test = square(test) → test 变成 inner 函数
↓
调用 test() → 执行 inner()
↓
inner() 中调用原 test() → 得到 2
↓
计算 2*2 → 返回 4
final_test = add(cube(square(test)))
# 实际引用关系:
add.inner.func -> cube.inner
cube.inner.func -> square.inner
square.inner.func -> 原始test
当调用 test() 时,实际调用的是最外层的 add.inner,执行链如下:
add.inner() → cube.inner() → square.inner() → test_original()
为什么看似调用一次却执行了所有装饰器?
装饰器的作用是构建调用链:
每个装饰器将原函数替换为一个新函数(如 inner),新函数内部会调用上一层函数。
调用最外层的 test()(即 add.inner())时,会依次触发整个调用链。
类比“洋葱模型”:
调用 test() → 剥开 add 层 → 剥开 cube 层 → 剥开 square 层 → 到达核心 test_original()
必须逐层穿透所有装饰器,才能执行到原始函数。
执行路径:
add.inner() → cube.inner() → square.inner() → test_original()
65 64 4 2
- 装饰器从下往上应用(靠近函数的先应用)
- 执行时从上往下调用(最外层的装饰器最先执行)
- 每个装饰器都会在调用链中被执行一次
- 返回值从内向外传递(原始函数→最内层装饰器→...→最外层装饰器)
总结
装饰器嵌套的本质:每个装饰器将原函数替换为一个新函数,形成层层包裹的调用链。
单次调用的触发逻辑:调用最外层函数时,会穿透所有装饰器层级,依次执行每个装饰器的增强逻辑。
核心原因:装饰器通过闭包保存了原始函数的引用,调用链的逐层展开是闭包和函数替换的必然结果。
3.lambda表达式(匿名函数)
1.定义
Lambda 表达式是 Python 中的匿名函数,用于创建简单的、一次性的函数对象。
语法:lambda arg1, arg2, arg3, ... argN : expression
lambda 参数列表: 表达式
参数列表:与普通函数参数类似,支持位置参数、默认参数、可变参数等。
表达式:只能有一个表达式,不能包含语句(如 return、if-else 代码块等),表达式的值即为返回值。
Lambda函数可以具有任意数量的参数,但只能有一个表达式。表达式被求值并返回。
# 普通函数:
def sum(a,b):
return a+b
print(sum(1,2))
# lambda表达式
sum=lambda a,b:a+b
print(sum(1,2))
lambda 是一个表达式,而非语句,所以它能够出现在 Python 语法不允许 def 语句出现的地方,这是它的最大优势。
2.Lambda表达式高级用法
1.多参数Lambda
#多参数
>>> mult=lambda x,y,z:x*y*z
>>> mult(2,3,4)
24
#默认参数
>>> power=lambda x,y=2:x**y
>>> power(3)
9
>>> power(3,3)
27
2.条件表达式
#简单条件判断
>>> check_even=lambda x:"偶数" if x%2==0 else "奇数"
>>> check_even(6)
'偶数'
>>> check_even(7)
'奇数'
#多条件判断
>>> categ=lambda x:("负数" if x<0 else "零" if x==0 else "正数")
>>> categ(-3)
'负数'
>>> categ(0)
'零'
>>> categ(5)
'正数'
3.高阶函数中应用
1>与 map() 结合
map(function, iterable,...)
将一个函数 应用于一个或多个可迭代对象的所有元素,并返回一个迭代器。
#提取字典中的特定值
>>> users = [{"name": "Alice", "age": 25}, {"name": "Bob", "age": 30}]
>>> map(lambda user: user["name"], users)
<map object at 0x0000020D47AFF908>
>>> list(map(lambda user: user["name"], users))
['Alice', 'Bob']
2>与 filter() 结合
filter(function or None, iterable)
使用指定函数过滤可迭代对象,只保留函数返回 True
的元素。
>>> filter(lambda x:x%2==0,range(10))
<filter object at 0x0000020D47AF9E88>
>>> list(filter(lambda x:x%2==0,range(10)))
[0, 2, 4, 6, 8]
>>> list(filter(lambda x:x%2,range(10)))
[1, 3, 5, 7, 9]
>>> list(filter(None,range(10)))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
3>与sorted()结合
sorted(iterable, key=None, reverse=False)
对可迭代对象进行排序,返回一个新的排序后的列表。
# 按字符串长度排序
words = ["apple", "banana", "cherry", "date"]
sorted_by_length = sorted(words, key=lambda x: len(x))
print(sorted_by_length) # 输出: ['date', 'apple', 'banana', 'cherry']
4.生成器
1.定义
生成器是 Python 中一种特殊的迭代器,允许按需生成值,而不是一次性生成所有值并存储在内存中。生成器使用 yield
语句而不是 return
来返回值。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 yield 方法时从当前位置继续运行。
def count():
i=0
while i<=5:
yield i
i+=1
count()
<generator object count at 0x00000181DBE90CF0>
for i in count():
print(i)
0
1
2
3
4
5
c=count()
next(c)
0
next(c)
1
next(c)
2
next(c)
3
next(c)
4
next(c)
5
next(c,'迭代完了')
'迭代完了'
next(c,'迭代完了')
'迭代完了'
注:生成器看作是一个制作机器,作用是每调用一次提供一个数据,并且会记住当时的状态;而列表、元组这些可迭代对象是容器,它们里面存放着早已准备好的数据。
生成器不走回头路(不支持下标索引),支持next()函数。
2.生成器表达式
类似于列表推导式,但使用圆括号
>>> (i**2 for i in range(5))
<generator object <genexpr> at 0x0000020D47AECDC8>
>>> t=(i**2 for i in range(5))
>>> next(t)
0
>>> next(t)
1
>>> next(t)
4
# 可以使用 for 循环迭代
>>> for value in t:
print(value)
9
16
>>> next(t)
Traceback (most recent call last):
File "<pyshell#30>", line 1, in <module>
next(t)
StopIteration
>>> next(t,'迭代完了')
'迭代完了'
>>> next(t,'迭代完了')
'迭代完了'
与列表推导式不同:列表推导式会一下将全部数据生产出来,并放到列表中,生成器表达式一次只生成一个。