当前位置:首页>python>从闭包到装饰器,彻底搞懂 Python 的函数高级用法

从闭包到装饰器,彻底搞懂 Python 的函数高级用法

  • 2026-09-09 14:34:41
从闭包到装饰器,彻底搞懂 Python 的函数高级用法

在 Python 中,函数是一等公民(first-class object)——这意味着函数可以像整数、字符串一样被赋值、传递、返回,甚至嵌套定义。正是这一特性,催生了闭包、装饰器、高阶函数等强大机制。

今天,我们就从最基础的函数嵌套出发,一步步揭开 闭包、装饰器、@classmethod 与 @staticmethod 的面纱。


一、函数嵌套:作用域的起点

Python 允许在函数内部定义另一个函数:

name = "武涛齐"defrun():    name = "alex"definner():        print(name)  # 访问外层函数的局部变量return innerv1 = run()  # 返回 inner 函数对象v1()        # 输出: alex
  • inner() 是 run() 的内部函数,对外部不可见,避免命名冲突。
  • 每次调用 run(),都会创建一个全新的 inner 函数实例(即使代码相同)。

这看似简单,却引出了一个关键概念:闭包(Closure)。


二、闭包(Closure):把数据“打包”带走

闭包 = 内部函数 + 外层函数的自由变量

当内部函数引用了外层函数的局部变量(如上面的 name),并且这个内部函数被返回或传递出去时,就形成了闭包。

✅ 闭包的核心价值:

  1. 封装数据:将状态“打包”进函数,避免污染全局命名空间;
  2. 延迟使用:数据在定义时捕获,在调用时才使用。

⚠️ 修改外层变量?你需要 nonlocal

默认情况下,内部函数只能读取外层变量,不能修改。若要修改,必须用 nonlocal 声明:

defouter():    x = 10definner():nonlocal x  # 声明 x 来自外层作用域        x = 20    inner()    print(x)  # 输出: 20

❌ 注意:nonlocal 只能用于嵌套函数中的外层函数作用域,不能用于全局变量。若想修改全局变量,请用 global。


三、作用域规则:LEGB 原则

Python 查找变量遵循 LEGB 规则:

  • Local(局部)→
  • Enclosing(闭包)→
  • Global(全局)→
  • Built-in(内置)
x = "global"defouter():    x = "enclosing"definner():        print(x)  # 输出 "enclosing"(Enclosing 优先于 Global)return inner

理解 LEGB,是掌握闭包和装饰器的前提。


四、装饰器:不改代码,增强功能

装饰器的本质是一个接收函数并返回新函数的高阶函数。

1. 函数式装饰器(最常用)

from functools import wrapsdefmy_decorator(func):    @wraps(func)  # 保留原函数的 __name__、__doc__ 等元信息defwrapper(*args, **kwargs):        print("Before function call")        result = func(*args, **kwargs)        print("After function call")return resultreturn wrapper@my_decoratordefgreet(name):"""Say hello"""returnf"Hello, {name}!"print(greet("Alice"))# 输出:# Before function call# After function call# Hello, Alice!

✅ 为什么用 @wraps?否则 greet.__name__ 会变成 "wrapper",影响调试和文档生成。


2. 类装饰器:用 __call__ 实现

classCountCalls:def__init__(self, func):        self.func = func        self.count = 0def__call__(self, *args, **kwargs):        self.count += 1        print(f"{self.func.__name__} 被调用了 {self.count} 次")return self.func(*args, **kwargs)@CountCallsdefsay_hi():    print("Hi!")say_hi()  # say_hi 被调用了 1 次say_hi()  # say_hi 被调用了 2 次

类装饰器适合需要维护状态(如计数、缓存)的场景。


3. 装饰器的常见用途

  • 📝 日志记录
  • ⏱️ 性能计时
  • 🔒 权限校验
  • 💾 缓存(memoization)
  • 🔄 自动重试

五、@classmethod vs @staticmethod:别再混淆!

特性
普通方法
@classmethod@staticmethod
第一个参数
self
(实例)
cls
(类)
无特殊参数
能否访问实例属性?
✅
❌
❌
能否访问类属性?
✅(通过 self.__class__)
✅
❌(除非硬编码类名)
调用方式
必须通过实例
类或实例
类或实例

✅ @classmethod 的典型用途:替代构造函数

classPerson:def__init__(self, name, age):        self.name = name        self.age = age    @classmethoddeffrom_string(cls, s):        name, age = s.split('-')return cls(name, int(age))  # 自动调用当前类的 __init__# 使用p = Person.from_string("Bob-25")

💡 优势:继承时自动适配子类(cls 指向实际调用的类)。

✅ @staticmethod 的定位:逻辑相关的工具函数

classMathUtils:    @staticmethoddefadd(a, b):return a + b# 调用MathUtils.add(1, 2)  # 3

它本质上就是一个放在类里的普通函数,只是语义上属于该类。


六、总结:一张表看懂所有

概念
核心思想
关键点
闭包
函数 + 外层变量的绑定
用 nonlocal 修改外层变量
装饰器
不侵入式增强函数
用 @wraps 保留元信息
@classmethod
操作类本身
首参 cls,适合工厂方法
@staticmethod
与类相关但无状态
无 self/cls,纯工具函数

👉 点击公众号主页【合集】→【Python 工程师修炼手册】,系统学习进阶知识。

最新文章

随机文章