上篇我们讲解了函数的函数概述、基础使用、参数类型、匿名函数,本篇聚焦变量作用域、递归函数两大进阶主题,,帮你进一步掌握函数式编程的精髓。
作用域(Scope)是指变量的有效作用范围,即在哪些代码中可以访问这个变量。
Python 中有 4 层作用域(按从内到外的顺序):
| 层级 | 说明 | 关键字 |
|---|---|---|
| L(Local) | 函数内部定义的变量,局部变量 | — |
| E(Enclosing) | 嵌套函数的外层函数变量 | nonlocal |
| G(Global) | 模块级别(文件顶部)全局变量 | global |
| B(Built-in) | Python 内置对象 | — |
💡 记忆口诀:L → E → G → B(由内向外,依次寻找)
在函数内部定义的变量,称为局部变量,只在函数内部有效:
def test():local_var="我是局部变量"print(local_var)test()# print(local_var) # ❌ 报错:name 'local_var' is not defined
在文件顶部(函数外部)定义的变量,称为全局变量,整个文件都可以访问:
global_var="我是全局变量"def test():print(global_var)test() # 调用函数,可以访问print(global_var) # 函数外也可以访问
输出:
我是全局变量我是全局变量
函数内部默认只能读取全局变量,不能直接修改:
count=10def increment():count=count+1# ❌ 报错:UnboundLocalErrorprint(count)
使用 global 关键字声明后,就可以在函数内修改全局变量:
count=10def increment():global count# 声明使用全局变量count=count+1print(count)increment() # 11print(count) # 11(全局变量被修改)
⚠️ 建议:尽量少用全局变量,函数之间传递参数更清晰、更安全。
nonlocal 用于嵌套函数中,修改外层(非全局)函数的变量:
def outer():num=10# outer 的局部变量def inner():nonlocal num# 声明使用外层函数的变量num=99print(f"inner: {num}")inner()print(f"outer: {num}")outer()
输出:
inner: 99outer: 99
如果去掉 nonlocal,inner 内部创建一个新的局部变量,原有 num 不受影响:
def outer():num=10def inner():num=99# 这里创建的是局部变量,不影响外层print(f"inner: {num}")inner()print(f"outer: {num}") # 仍是 10outer()
输出:
inner: 99outer: 10
| 关键字 | 作用范围 | 可读 | 可写 |
|---|---|---|---|
| 不声明 | 局部变量 | ✅ | ✅(创建新局部变量) |
global | 全局变量 | ✅ | ✅ |
nonlocal | 外层函数变量 | ✅ | ✅ |
递归就是函数自己调用自己。就像俄罗斯套娃,一个套着一个,直到最小的那一个为止。
递归由两部分组成:
递归出口:什么时候停止(最基础的情况)
递归调用:函数继续调用自己
需求:计算 n! = n × (n-1) × (n-2) × ... × 1
递归分析:
递归出口:n = 1 时,返回 1
递归调用:n! = n × (n-1)!
# 普通实现def factorial(n):result = 1for i in range(1, n+1):result *= ireturn result# 递归实现def factorial_recursive(n):if n == 1:return 1# 递归出口return n*factorial_recursive(n-1) # 递归调用print(factorial_recursive(5)) # 120
递归调用过程(n=5):
factorial_recursive(5) → 5 * factorial_recursive(4) → 4 * factorial_recursive(3) → 3 * factorial_recursive(2) → 2 * factorial_recursive(1) → 1 ← 递归出口 → 2 * 1 = 2 → 3 * 2 = 6 → 4 * 6 = 24→ 5 * 24 = 120
斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, ...
规律:F(n) = F(n-1) + F(n-2),且 F(1)=1, F(2)=1
def fibonacci_iter(n):if n in (1, 2):return 1a, b=1, 1for _ in range(3, n+1):c=a+ba=bb=creturn b# 输出前10个斐波那契数for i in range(1, 11):print(f"F({i}) = {fibonacci_iter(i)}")
输出:
F(1) = 1F(2) = 1F(3) = 2F(4) = 3F(5) = 5F(6) = 8F(7) = 13F(8) = 21F(9) = 34F(10) = 55
⚠️ 递归必须设定出口,否则会导致无限递归,引发 RecursionError。
# 危险:无递归出口def bad_recursive(n):return n+bad_recursive(n-1)# bad_recursive(5) # ❌ RecursionError: maximum recursion depth exceeded
递归 vs 循环:
| 维度 | 递归 | 循环 |
|---|---|---|
| 代码简洁度 | ✅ 更简洁 | 一般 |
| 性能 | 较差(函数调用开销) | ✅ 更高 |
| 内存占用 | 较高(调用栈) | ✅ 更低 |
| 适用场景 | 问题本身具有递归结构 | 一般迭代问题 |
💡 建议:能用循环解决的问题,优先用循环。递归适合树形结构、分治等问题。
上述斐波那契递归有大量重复计算,可以用记忆化(Memoization)优化:
# 使用字典手动记忆化memo= {}def fib(n):# 递归出口:第1、2项都是 1if n<=2:return1# 如果已经算过,直接从缓存拿,不用再算if n in memo:return memo[n]# 没算过就计算,并存入缓存result = fib(n-1) +fib(n-2)memo[n] = result# 存起来return result# 输出前10项for i in range(1, 11):print(f"F({i}) = {fibonacci_iter(i)}")