理解内部函数的定义与基本用法
掌握变量作用域规则及 nonlocal 关键字的使用
认识闭包的概念及其工作原理
了解内部函数在封装、装饰器等实际场景中的应用价值
在 Python 中,内部函数(也称嵌套函数)是指在一个函数内部定义的另一个函数。这种结构不仅有助于代码组织,还能实现逻辑封装、变量捕获和高级功能(如闭包与装饰器)。
def outer(msg):
def inner():
print(msg) # 访问外层函数的变量
inner()
outer("Hello")输出:
Hello说明:inner() 是 outer() 的内部函数,它能直接访问外层函数的参数 msg,这体现了 Python 的词法作用域特性。
Python 使用 LEGB 规则解析变量名:
Local(局部):当前函数内的变量
Enclosing(封闭):外层函数的变量(嵌套函数可访问)
Global(全局):模块级别的变量
Built-in(内置):Python 内置名称(如 len, print)
内部函数默认可以读取外层函数的变量,但若要修改它们,则需使用 nonlocal 关键字。
def outer():
message = "Tech for dev"
def inner():
print(message)
inner()
outer()输出:
Tech for dev说明:inner() 成功访问了 outer() 中定义的局部变量 message。
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 count,count = 20 会在 inner() 内创建一个新的局部变量,不会影响外层的 count。
nonlocal 明确告诉 Python:我们要修改的是外层作用域中的变量。
注意:
nonlocal不能用于修改全局变量,此时应使用global。
闭包是指内部函数在其外层函数执行完毕后,仍能“记住”并访问外层函数的变量。
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" 的引用。这就是闭包。
闭包的三个条件:
存在嵌套函数
内部函数引用了外层函数的变量
外层函数返回了内部函数
将仅在某个函数内部使用的辅助逻辑隐藏起来,避免污染全局命名空间。
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']优势:
clean 和 validate 仅在 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说明:
wrapper 是 log_calls 的内部函数
它在调用原函数前后插入日志逻辑
通过 @log_calls 语法糖,实现对 multiply 的透明增强
内部函数可以访问外层函数的变量(遵循 LEGB 规则)
使用 nonlocal 可修改外层函数的变量(非全局)
闭包使得内部函数能“记住”外层作用域的状态,即使外层函数已退出
内部函数广泛用于:
封装私有逻辑(避免全局污染)
构建装饰器(增强函数行为)
实现工厂函数(动态生成带状态的函数)
合理使用内部函数可提升代码的模块性、可读性和复用性
如果在内部函数中既不使用 nonlocal 也不使用 global,直接对同名变量赋值,会发生什么?请举例说明。
闭包在内存管理上有什么需要注意的地方?是否存在潜在的内存泄漏风险?
尝试用内部函数实现一个“计数器工厂”:每次调用返回一个新的计数器函数,该函数每次调用自身时返回递增的整数(从 1 开始)。