python函数-闭包,装饰器,lambda表达式,生成器

目录

1.闭包

1.闭包定义

2.闭包的__closure__属性

2.装饰器

1.装饰器定义

2.无参装饰器

3.带参装饰器

4.多个装饰器用在同个函数

3.lambda表达式(匿名函数)

1.定义

2.Lambda表达式高级用法

1.多参数Lambda

2.条件表达式

3.高阶函数中应用

1>与 map() 结合

2>与 filter() 结合

3>与sorted()结合

4.生成器

1.定义

2.生成器表达式


1.闭包

1.闭包定义

闭包(Closure):当一个内部函数引用了外部函数的变量时,即使外部函数已执行完毕,这些变量仍会被保留在内存中,形成闭包。

三要素

  1. 嵌套函数(内部函数定义在外部函数内)
  2. 内部函数引用外部函数的变量
  3. 外部函数返回内部函数
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)时:

  1. Python检查inner函数的作用域,在局部作用域找不到exp
  2. 查找闭包环境(__closure__),找到绑定的exp=2
  3. 完成计算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

  1. 装饰器从下往上应用(靠近函数的先应用)
  2. 执行时从上往下调用(最外层的装饰器最先执行)
  3. 每个装饰器都会在调用链中被执行一次
  4. 返回值从内向外传递(原始函数→最内层装饰器→...→最外层装饰器)

总结

装饰器嵌套的本质:每个装饰器将原函数替换为一个新函数,形成层层包裹的调用链。

单次调用的触发逻辑:调用最外层函数时,会穿透所有装饰器层级,依次执行每个装饰器的增强逻辑。

核心原因:装饰器通过闭包保存了原始函数的引用,调用链的逐层展开是闭包和函数替换的必然结果。

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,'迭代完了')
'迭代完了'

与列表推导式不同:列表推导式会一下将全部数据生产出来,并放到列表中,生成器表达式一次只生成一个。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值