匿名函数
lambda函数
匿名函数(也称为lambda函数)是一种可以在需要时快速定义的小型函数。
1 | sq = lambda x: x * x |
这里用到的 lambda
表达式与下面的函数定义有着相同的功能:
1 | def sq(x): |
lambda表达式的基本语法如下:
lambda arg1,arg2,arg3...: <表达式>
其arg1/arg2/arg3为函数的参数
<表达式>相当于函数体
函数返回值:表达式的计算结果
注意:
- lambda实际生成了一个函数对象。
- lambda表达式只允许包含一个表达式。
lambda函数的使用:
可以作为另一个函数的参数传递,用于定制特定的行为。
1 | nums = [1, 2, 3, 4, 5] |
参数中可以使用lambda作为参数的还有map()
等。
使用lambda
表达式反转拼写,然后依此给单词列表排序
1 | fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana'] |
除了作为参数传递给高阶函数之外,Python 很少使用匿名函数。
lambda
句法只是语法糖:与
def
语句一样,lambda
表达式会创建函数对象。
总结:
- 匿名函数是Python中的一种快速定义小型函数的方式
- 它没有函数名,使用
lambda
关键字定义 - 匿名函数可以简化代码、作为函数参数传递以及在列表推导中应用
闭包
- 闭包的定义 在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
- 闭包的构成条件 在函数嵌套的前提下 内部函数使用了外部函数的变量或者参数 外部函数返回了内部函数
- 闭包的作用 可以保存外部函数内的变量,不会随着外部函数调用完而销毁 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。
1 | #在函数嵌套的前提下 |
举例:
假如有个名为 avg 的函数,它的作用是计算不断增加的系列值的均值。
例如,商品历史周期内的平均价格,每天添加一个新的价格,目前为止均价的计算要考虑商品全部价格。
1 | # 计算移动平均值的类 |
averager
是定义在 make_averager
内部的函数,形成了闭包
在averager内部,series是一个自由变量。
这是一个技术术语,指未在本地作用域中绑定的变量。

1 | '''Python 在 __code__ 属性(表示编译后的函数定义体)中 |
示例:
创建一个闭包实现计数器
编写一个闭包函数,它应该提供一个计数器功能。每次调用该闭包时,它应返回下一个整数。
例如,首次调用返回 1,下一次调用返回 2,依此类推。
提示: 使用一个非局部变量来存储当前计数。
1 | counter() 1 |
1 | def create_counter(): |
总结
闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。
注意,只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量。
装饰器
装饰器是 Python 中一种强大且灵活的语法结构,本质上是一个闭包函数。它可以在不改变原函数代码和调用方式的前提下,为函数增添新的功能。这就好比给一个物品添加装饰,在不改变物品本身核心功能的基础上,让它具备更多特性。
装饰器在代码的可维护性、复用性和可读性方面都有很大帮助,广泛应用于日志记录、性能测试、权限验证等场景。
- 是一种函数,有内置的装饰器,也可以自定义装饰器,本质上就是一个闭包函数。
- 可以在不改变函数调用方式的情况下给函数增加对应功能
- 通过
@
加上命名进行使用,作用在函数声明的前面,如下:
1 |
|
简单来说,装饰器函数接收一个函数作为参数,然后返回一个新的函数。新函数通常会在执行原函数前后添加额外的逻辑,从而实现功能增强。
内置装饰器示例
Python 有一些内置装饰器,例如@property
,它用于将类中的方法转换为属性调用的形式,让代码更加简洁直观。
1 | class Person: |
@property
装饰器把age
方法装饰成了一个属性,调用person.age
时,实际上调用的是被装饰的age
方法,但看起来就像在访问一个普通属性,提升了代码的易用性。
自定义装饰器示例
下面通过一个简单的例子来展示如何自定义装饰器,实现函数执行时间的计算功能。
1 | import time |
- 定义装饰器函数
timer
timer
函数接收一个函数func
作为参数,这是装饰器的基本形式,即接收被装饰的函数。- 内部定义了一个
wrapper
函数,wrapper
函数就是闭包函数,它可以访问外部函数timer
作用域内的func
变量。 - 在
wrapper
函数中,首先记录开始时间start_time = time.time()
,然后调用原函数func()
,再记录结束时间end_time = time.time()
,最后计算并打印函数执行的耗时。 timer
函数最后返回wrapper
函数对象。
- 使用装饰器
@timer
语法将timer
装饰器应用到say_hello
函数上,这等同于执行say_hello = timer(say_hello)
。- 调用
say_hello()
时,实际上调用的是wrapper
函数,wrapper
函数会在执行say_hello
函数的前后添加计算时间的逻辑,从而实现了在不改变say_hello
函数本身代码和调用方式的情况下,为其增添了计算执行时间的功能。
带参数的装饰器
有时候我们需要给装饰器传递参数,来定制不同的装饰逻辑。下面是一个带参数的装饰器示例,用于根据不同的日志级别打印函数调用信息。
1 | def logger(level): |
- 定义带参数的装饰器外层函数
logger
logger
函数接收一个参数level
,用于指定日志级别。- 它返回另一个函数
decorator
,decorator
函数才是真正接收被装饰函数的装饰器函数。
decorator
函数- 接收被装饰的函数
func
,内部定义wrapper
函数。 wrapper
函数根据传入的level
参数进行不同的日志打印,然后调用原函数func
。decorator
函数返回wrapper
函数。
- 接收被装饰的函数
- 使用带参数的装饰器
@logger(level="INFO")
这种形式先调用logger
函数并传入level
参数,得到具体的装饰器函数,再将其应用到greet
函数上。这样就实现了根据不同参数定制装饰器行为的功能。
总结:
- 装饰器可以在不改变原函数的定义和调用方式的基础上,增强函数的功能。
- 严格来说,装饰器只是语法糖。装饰器可以像常规的可调用对象那样调用,其参数是另一个函数。
- 装饰器能把被装饰的函数替换成其他函数。