Python 函数终极进阶:闭包与装饰器,给函数穿上“马甲”
文章目
前言
函数这块我们已经聊了好几期了,从最基础的“什么是函数”,到参数的各种花式传法,再到作用域、递归、高阶函数……今天终于来到了函数系列的收官之战。
闭包和装饰器是 Python 里两个“听起来很高级、用起来很优雅”的概念。
闭包:让函数“记住”它诞生时的环境,即使离开了那个环境,记忆还在。
装饰器:在不修改原函数代码的前提下,给函数“穿上马甲”增加新功能,俗称“偷懒界的扛把子”。
学完今天这一篇,你的代码不仅跑得快,还长得好看。Python 里的 @staticmethod、@classmethod、Flask 里的 @app.route(),全是装饰器的杰作。
先花 30 秒快速回顾一下前面讲过的函数知识,然后重点攻克闭包和装饰器。开整!🧵
第一章:函数基础快速回顾(思路清晰整理基础)
1.1 什么是函数?
函数就是把一段代码打包,起个名字,随时调用。写一次用无数次,告别 Ctrl+C/V。
python
def greet(name):
return f"你好,{name}!"
1.2 函数的参数(四种传法)
| 参数类型 | 说明 | 示例 |
|---|---|---|
| 位置参数 | 按顺序传 | greet("王小明", 18) |
| 默认参数 | 不传就用默认值 | def greet(name, age=18) |
| 关键字参数 | 指名道姓传 | greet(age=20, name="小红") |
| 可变参数 | 收不定数量的参数 | *args 收元组,**kwargs 收字典 |
python
# 万能参数模板
def super_func(*args, **kwargs):
print("位置参数们:", args) # 元组
print("关键字参数们:", kwargs) # 字典
super_func(1, 2, 3, name="小明", age=18)
1.3 变量作用域
| 关键字 | 作用 |
|---|---|
global |
在函数里修改全局变量 |
nonlocal |
在嵌套函数里修改外层函数的变量 |
1.4 递归与高阶函数
递归:函数自己调自己,必须设终止条件
高阶函数:把函数当参数传来传去,比如 map()、filter()、sorted()
第二章:闭包 —— 函数里的“记忆面包”
2.1 什么是闭包?
闭包就是在一个函数内部定义了另一个函数,并且内部函数使用了外部函数的变量,然后外部函数把内部函数返回。
听起来有点绕?直接看代码:
python
def outer(x): # 外部函数
def inner(y): # 内部函数
return x + y # 内部函数使用了外部函数的 x
return inner # 把内部函数返回
# 创建闭包
add_5 = outer(5) # outer(5) 返回 inner,并且 x=5 被“记住”了
print(add_5(3)) # 8 (5 + 3)
print(add_5(10)) # 15 (5 + 10)
add_10 = outer(10) # 新建一个闭包,x=10
print(add_10(3)) # 13 (10 + 3)
📌 闭包的核心:
inner函数“记住”了outer里的x,即使outer已经执行完了,x的值还活着,被inner随身携带。
就像你小时候妈妈给你织了一条围巾,里面带着妈妈的手温——就算妈妈不在身边,围巾里还留着她的温度。
2.2 闭包的条件
形成闭包需要满足三个条件:
-
函数嵌套:外层函数里定义了内层函数
-
内层引用外层变量:内层函数用了外层的变量
-
外层返回内层函数:外层函数把内层函数当作返回值返回
2.3 闭包有啥用?
闭包最常见的用途就是“保存状态”——让函数在执行完后还能记住一些东西。
💡 每次调用
counter()都会创建一个新的闭包,里面的count是独立的。这就像你去银行开账户,每个账户都有自己的余额。
第三章:装饰器 —— 给函数“穿上马甲”
3.1 装饰器长啥样?
装饰器是 Python 里一个很酷的特性,它让你能在不修改原函数代码的前提下,给函数增加新的功能。
python
def timer(func):
import time
def wrapper():
start = time.time()
func()
end = time.time()
print(f"执行耗时:{end - start:.4f} 秒")
return wrapper
@timer # 👈 装饰器语法糖
def slow_function():
import time
time.sleep(1)
print("任务执行完毕")
slow_function()
输出:
text
任务执行完毕 执行耗时:1.0002 秒
📌
@timer相当于执行了slow_function = timer(slow_function),把原函数“包”了一层。
3.2 装饰器的本质
装饰器本质上就是一个高阶函数:它接收一个函数作为参数,返回一个新的函数(通常叫 wrapper),新函数里包裹了原函数,并在执行前后增加额外逻辑。
手写一个最简装饰器(无参数版):
python
def my_decorator(func):
def wrapper():
print("函数执行前——准备开工!")
func()
print("函数执行后——收工!")
return wrapper
@my_decorator
def say_hello():
print("你好呀!")
say_hello()
输出:
text
函数执行前——准备开工! 你好呀! 函数执行后——收工!
3.3 带参数的装饰器(装饰器工厂)
有时候你希望装饰器本身能接收参数,比如“重复执行 N 次”:
python
这样可以记录函数返回所需要用的时间
import time
from random import randint as sj
def jv(a):
def zhs(*args):
start = time.time()
zj = a(*args)
print(f"开始反馈时间开销")
print(f"{time.time() - start}")
return zj
return zhs
def sy(x,y,z):
x = x +y
y = y +z
z = z +x
return x + y + z
print(sy(10,20,30))
💡 带参数的装饰器等于再多包一层:索引jv() 先返回 zhs, 再装饰 sy(x,y,z)。
3.4 多个装饰器叠加
一个函数可以被多个装饰器同时装饰,执行顺序是从下往上:
可以反应出来一个函数的消耗时长和运行具体时间
python
import time
from random import randint as sj
def jv(a):
def zhs(*args):
start = time.time()
zj = a(*args)
print(f"开始反馈时间开销")
print(f"{time.time() - start}")
return zj
return zhs
import datetime
def cb(aa):
def js(*ss):
jg = aa(*ss)
aaa = datetime.datetime.now().strftime("%Y年-%m月-%d日-%H时:%M分:%S秒")
print(f"执行时间为{aaa} ,数据值有{ss},输出结果为{jg}")
return jg
return js
@cb
@jv
def sy(x,y,z):
x = x +y
y = y +z
z = z +x
return x + y + z
print(sy(10,20,30))
3.5 装饰器常用场景
| 场景 | 装饰器名字(自拟) | 作用 |
|---|---|---|
| 计时 | @timer |
统计函数执行时间 |
| 日志 | @log |
自动打印调用信息和参数 |
| 权限校验 | @login_required |
检查用户是否登录 |
| 缓存 | @cache |
缓存函数结果,避免重复计算 |
| 重试 | @retry |
失败后自动重试 N 次 |
| 限流 | @rate_limit |
控制函数调用频率 |
🎯 闭包与装饰器对比
| 闭包 | 装饰器 | |
|---|---|---|
| 本质 | 函数嵌套 + 引用外部变量 | 闭包的一种“语法糖”应用 |
| 目的 | 让函数记住状态 | 给函数加功能,不改原代码 |
| 语法 | 手动返回内层函数 | 用 @ 符号,更优雅 |
| 例子 | 计数器、配置工厂 | @timer、@log、@login_required |
结尾
到今天为止,你的函数技能树已经点满了:
基础:定义、调用、参数、返回值
进阶:作用域、递归、高阶函数、lambda
高级:闭包、装饰器
闭包让函数“带记忆”,装饰器让函数“穿马甲”——这两个东西组合起来,你在 Python 世界里已经可以横着走了。
你现在能看懂那些 @app.route('/') 是咋回事了,能自己写一个 @timer 量一下哪段代码跑得慢,还能写一个 @login_required 拦住没登录的用户。
python
# 收工前的彩蛋:你自己也能写出这种“看起来很牛”的代码
@timer
@log
def your_first_decorated_func():
print("你已经是进阶选手了!")
走到这一步,函数这块你已经不是新手了。后面再遇到新东西,你会感觉“万变不离其宗”。
🎁 彩蛋:写给一路打怪升级的你
好,说真的——
如果你是从 print("Hello World") 那一篇一直跟到现在的,那你自己可能没意识到,但你已经在 Python 这条路上走了挺远了。
从最开始连“环境变量”是啥都得查半天,到今天 闭包看得懂,装饰器写得出来,还给函数穿上马甲……你回头看看第一篇,你会发现自己早就不是那个连 int() 和 str() 都分不清的小白了。
函数这块学完,相当于你王者荣耀里把“法师”这个位置彻底练透了——走位、技能连招、支援节奏,全都有数了。后面再学面向对象、模块、异常处理、文件操作,都会觉得:“哦,原来是这么回事。”
Python 的函数写得好的程序员,写出来的代码读起来像说话一样流畅,逻辑是顺的,层次是清的,别人接手你的代码会忍不住说一句:“这代码写得真舒服。”
这句话,早晚有一天会落到你头上。
不急,按自己的节奏慢慢来,代码这东西,你越写越有感觉,越写越顺手。今天搞懂一个概念,明天就多一个武器。
写代码不是靠天赋,是靠“搞懂了之后再敲几遍”。
你敲过的每一行代码,都在帮你从“看得懂”走向“写得出”,再到“写得好”。
你现在站在函数这一关的终点,也是面向对象那一关的起点。前方还有新地图,你手里的剑已经够亮了。
去敲吧,Python 糕手。🍻🐍
—— 下个路口见。 👋
更多推荐



所有评论(0)