源本科技 | 码上会

Python 嵌套函数

2026/01/18
6
0

学习目标

  • 理解内部函数的定义与基本用法

  • 掌握变量作用域规则及 nonlocal 关键字的使用

  • 认识闭包的概念及其工作原理

  • 了解内部函数在封装、装饰器等实际场景中的应用价值


什么是内部函数

在 Python 中,内部函数(也称嵌套函数)是指在一个函数内部定义的另一个函数。这种结构不仅有助于代码组织,还能实现逻辑封装、变量捕获和高级功能(如闭包与装饰器)。

def outer(msg):
    def inner():
        print(msg)  # 访问外层函数的变量
    inner()

outer("Hello")

输出:

Hello

说明:
inner()outer() 的内部函数,它能直接访问外层函数的参数 msg,这体现了 Python 的词法作用域特性。


变量作用域与 LEGB 规则

Python 使用 LEGB 规则解析变量名:

  • Local(局部):当前函数内的变量

  • Enclosing(封闭):外层函数的变量(嵌套函数可访问)

  • Global(全局):模块级别的变量

  • Built-in(内置):Python 内置名称(如 len, print

内部函数默认可以读取外层函数的变量,但若要修改它们,则需使用 nonlocal 关键字。


示例 1:读取外层变量

def outer():
    message = "Tech for dev"
    def inner():
        print(message)
    inner()

outer()

输出:

Tech for dev

说明:
inner() 成功访问了 outer() 中定义的局部变量 message


示例 2:nonlocal 修改外层变量

def outer():
    count = 10
    def inner():
        nonlocal count
        count = 20
        print("Inside inner:", count)
    inner()
    print("After inner:", count)

outer()

输出:

Inside inner: 20
After inner: 20

关键点:

  • 若没有 nonlocal countcount = 20 会在 inner() 内创建一个新的局部变量,不会影响外层的 count

  • nonlocal 明确告诉 Python:我们要修改的是外层作用域中的变量

注意:nonlocal 不能用于修改全局变量,此时应使用 global


示例 3:闭包

闭包是指内部函数在其外层函数执行完毕后,仍能“记住”并访问外层函数的变量

def make_greeter(greeting):
    def greeter(name):
        return f"{greeting}, {name}!"
    return greeter

say_hello = make_greeter("Hello")
print(say_hello("Alice"))

输出:

Hello, Alice!

说明:
即使 make_greeter("Hello") 已经返回并结束执行,返回的 greeter 函数仍然保留了对 greeting"Hello" 的引用。这就是闭包

闭包的三个条件:

  1. 存在嵌套函数

  2. 内部函数引用了外层函数的变量

  3. 外层函数返回了内部函数


实际应用场景

封装辅助逻辑(提高内聚性)

将仅在某个函数内部使用的辅助逻辑隐藏起来,避免污染全局命名空间。

def process_data(raw_data):
    def clean(item):
        return item.strip().lower()
    
    def validate(item):
        return len(item) > 0
    
    cleaned = [clean(x) for x in raw_data]
    return [x for x in cleaned if validate(x)]

result = process_data(["  Python  ", "  ", "INNER FUNC"])
print(result)

输出:

['python', 'inner func']

优势:

  • cleanvalidate 仅在 process_data 内部可见,外部无法调用

  • 主函数逻辑清晰,职责单一


实现装饰器

装饰器本质上是返回内部函数的高阶函数,常用于日志、计时、权限控制等横切关注点。

import functools

def log_calls(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned {result}")
        return result
    return wrapper

@log_calls
def multiply(x, y):
    return x * y

multiply(3, 4)

输出:

Calling multiply with args=(3, 4), kwargs={}
multiply returned 12

说明:

  • wrapperlog_calls 的内部函数

  • 它在调用原函数前后插入日志逻辑

  • 通过 @log_calls 语法糖,实现对 multiply 的透明增强


重点总结

  • 内部函数可以访问外层函数的变量(遵循 LEGB 规则)

  • 使用 nonlocal 可修改外层函数的变量(非全局)

  • 闭包使得内部函数能“记住”外层作用域的状态,即使外层函数已退出

  • 内部函数广泛用于:

    • 封装私有逻辑(避免全局污染)

    • 构建装饰器(增强函数行为)

    • 实现工厂函数(动态生成带状态的函数)

  • 合理使用内部函数可提升代码的模块性、可读性和复用性


思考题

  1. 如果在内部函数中既不使用 nonlocal 也不使用 global,直接对同名变量赋值,会发生什么?请举例说明。

  2. 闭包在内存管理上有什么需要注意的地方?是否存在潜在的内存泄漏风险?

  3. 尝试用内部函数实现一个“计数器工厂”:每次调用返回一个新的计数器函数,该函数每次调用自身时返回递增的整数(从 1 开始)。