lesson17:Python函数之递归、匿名函数与变量作用域

目录

引言

 一、递归函数:用自身解构复杂问题

1. 递归的基本结构

2. 递归的典型应用场景

3. 递归的优缺点与优化

二、匿名函数:用lambda实现“一句话函数”

1. lambda与普通函数的区别

2. lambda的典型应用

3. lambda的局限性

三、变量作用域:理解LEGB规则

1、LEGB规则的深度解析

(1)Local(局部作用域)

(2)Enclosing(嵌套作用域)

(3)Global(全局作用域)

(4)Built-in(内置作用域)

2. 修改作用域的高级关键字:global与nonlocal

(1)global:声明全局变量

(2)nonlocal:声明嵌套作用域变量

3. 常见作用域陷阱

 总结


 引言

函数是Python编程的核心基石,它不仅实现了代码的模块化与复用,更通过高级特性如递归、匿名函数和灵活的作用域管理,赋予开发者简洁高效的问题解决能力。本文将深入探讨递归函数的逻辑与应用、匿名函数的简洁之美,以及变量作用域的底层规则,帮助你构建更优雅、更健壮的Python代码。


 一、递归函数:用自身解构复杂问题

递归函数是指在函数体内直接或间接调用自身的函数。它的核心思想是将复杂问题分解为与原问题结构相似的子问题,通过逐步简化问题规模,最终达到可直接求解的“基线条件”。

1. 递归的基本结构

一个合法的递归函数必须包含两部分:

  • 基线条件(Base Case):终止递归的条件,避免无限循环。
  • 递归条件(Recursive Case):调用自身解决规模更小的子问题。

示例1:计算n的阶乘(n!)
阶乘的数学定义为:n! = n × (n-1) × ... × 1,且0! = 1。

def factorial(n):
# 基线条件:n=0时返回1
if n == 0:
return 1
# 递归条件:n! = n × (n-1)!
return n * factorial(n-1)


print(factorial(5)) # 输出:120

2. 递归的典型应用场景

  • 数学问题:斐波那契数列、汉诺塔问题、幂运算等。
  • 数据结构遍历:树的深度优先搜索(DFS)、链表反转等。
  • 分治算法:快速排序、归并排序等。

示例2:斐波那契数列(第n项)
斐波那契数列定义:F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2)。

def fibonacci(n):
if n <= 1: # 基线条件:n=0返回0,n=1返回1
return n
return fibonacci(n-1) + fibonacci(n-2) # 递归条件


print(fibonacci(6)) # 输出:8(数列:0,1,1,2,3,5,8...)

3. 递归的优缺点与优化

  • 优点:逻辑简洁,代码可读性高(如树结构遍历)。
  • 缺点
    • 递归深度有限制(Python默认递归深度约为1000,超过会抛出RecursionError)。
    • 重复计算问题(如斐波那契数列的递归实现存在大量重复计算)。
  • 优化方案
    • 尾递归优化:将递归调用放在函数返回的最后一步(但Python解释器不支持尾递归优化)。
    • 记忆化搜索:用字典缓存已计算结果,避免重复计算。
    # 优化后的斐波那契(记忆化搜索)
    memo = {0: 0, 1: 1}
    def fibonacci_optimized(n):
    if n not in memo:
    memo[n] = fibonacci_optimized(n-1) + fibonacci_optimized(n-2)
    return memo[n]

二、匿名函数:用lambda实现“一句话函数”

匿名函数(lambda)是一种无需定义函数名的简洁函数形式,适用于逻辑简单、仅需一行代码的场景。它的语法为:

lambda 参数列表: 表达式  # 表达式结果即返回值

1. lambda与普通函数的区别

特性 lambda函数 普通函数(def定义)
命名 匿名,无需函数名 必须指定函数名
代码量 仅一行表达式 可包含多行代码和复杂逻辑
返回值 自动返回表达式结果 需显式使用return语句
使用场景 临时、简单逻辑 复杂逻辑、可复用场景

2. lambda的典型应用

  • 作为高阶函数参数:配合map()filter()sorted()等函数实现简洁逻辑。

    # 示例:用sorted()对字典按值排序(lambda作为key参数)
    students = {"Alice": 90, "Bob": 85, "Charlie": 95}
    sorted_students = sorted(students.items(), key=lambda x: x[1], reverse=True)
    print(sorted_students) # 输出:[('Charlie', 95), ('Alice', 90), ('Bob', 85)]
  • 简化条件判断:替代简单的if-else逻辑。

    # 示例:根据分数返回等级(A/B/C/D)
    get_grade = lambda score: "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 60 else "D"
    print(get_grade(88)) # 输出:B
  • 闭包与回调函数:在函数式编程中作为轻量级回调。

3. lambda的局限性

  • 仅支持单个表达式,无法包含循环、条件语句块(如if-elif-else需用三元表达式简化)。
  • 逻辑复杂时可读性差,建议优先使用普通函数。

三、变量作用域:理解LEGB规则

变量作用域指变量的可访问范围。Python通过LEGB规则确定变量查找顺序,即:Local(局部)→ Enclosing(嵌套)→ Global(全局)→ Built-in(内置)

1、LEGB规则的深度解析

LEGB规则(Local → Enclosing → Global → Built-in)是变量查找的“黄金法则”,但在实际场景中可能因复杂嵌套或关键字修饰而产生例外。

(1)Local(局部作用域)

函数内部定义的变量(包括参数)默认属于局部作用域,仅在函数执行期间有效。

关键特性

  • 函数参数视为局部变量,例如def func(a): print(a)中的a
  • 局部变量与全局变量同名时,局部变量会“遮蔽”(Shadowing)全局变量,即优先使用局部定义。

示例:局部变量遮蔽全局变量

x = 100 # 全局变量
def func():
x = 200 # 局部变量,遮蔽全局x
print("局部x:", x) # 输出:局部x: 200
func()
print("全局x:", x) # 输出:全局x: 100(全局x未被修改)
(2)Enclosing(嵌套作用域)

当函数嵌套时,内层函数可以访问外层函数定义的变量(非全局变量),这些变量属于嵌套作用域。

注意:外层函数的变量默认不可被内层函数修改,除非使用nonlocal关键字。

示例:未使用nonlocal的嵌套作用域

def outer():
x = 10
def inner():
x = 20 # 此处x为inner的局部变量,不影响outer的x
print("inner局部x:", x) # 输出:inner局部x: 20
inner()
print("outer嵌套x:", x) # 输出:outer嵌套x: 10(未被修改)


outer()
(3)Global(全局作用域)

模块级定义的变量(函数外定义)属于全局作用域,可被模块内所有函数访问。若需在函数内修改全局变量,必须用global声明。

常见误区

  • 仅读取全局变量时无需声明global,但修改时必须声明,否则会被视为局部变量。

示例:正确修改全局变量

x = 10
def func():
global x # 声明x为全局变量
x = 20 # 修改全局x
func()
print(x) # 输出:20(全局x被成功修改)
(4)Built-in(内置作用域)

Python内置的函数和常量(如lenlistTrue)存储在内置命名空间,优先级最低。若用户定义的变量与内置名称冲突,会遮蔽内置对象。

风险示例:遮蔽内置函数

len = 10 # 全局变量遮蔽内置len()
print(len([1,2,3])) # 报错:TypeError: 'int' object is not callable

2. 修改作用域的高级关键字:globalnonlocal

(1)global:声明全局变量
  • 作用:将函数内的变量绑定到全局命名空间。
  • 适用场景:需在函数内修改模块级全局变量。

示例:全局变量的跨函数共享

count = 0 # 全局计数器


def increment():
global count
count += 1


def decrement():
global count
count -= 1


increment()
increment()
decrement()
print(count) # 输出:1
(2)nonlocal:声明嵌套作用域变量
  • 作用:将内层函数的变量绑定到最近的外层非全局作用域(即嵌套作用域)。
  • 适用场景:嵌套函数中,内层函数需修改外层函数的变量。

示例:用nonlocal实现计数器工厂

def make_counter(init=0):
count = init # 外层函数变量(嵌套作用域)
def counter():
nonlocal count # 绑定到外层count
count += 1
return count
return counter


counter1 = make_counter(10)
print(counter1()) # 输出:11
print(counter1()) # 输出:12


counter2 = make_counter(100)
print(counter2()) # 输出:101(独立于counter1)

global vs nonlocal 对比

关键字 绑定目标 适用场景
global 全局命名空间 修改模块级全局变量
nonlocal 最近的外层非全局作用域 嵌套函数中修改外层函数变量

3. 常见作用域陷阱

  • 局部变量遮蔽全局变量:函数内定义与全局变量同名的局部变量时,局部变量会遮蔽全局变量。

    x = 10
    def func():
    x = 20 # 局部变量x,不影响全局x
    print(x) # 输出:20
    func()
    print(x) # 输出:10(全局x未变)
  • 未声明global直接修改全局变量:会报错UnboundLocalError

    x = 10
    def func():
    x += 1 # 错误:未声明global,x被视为局部变量但未定义
    func() # 报错:UnboundLocalError: local variable 'x' referenced before assignment

 总结

  • 递归函数通过“自调用”解构复杂问题,需注意基线条件与递归深度限制,必要时用记忆化优化性能。
  • 匿名函数(lambda) 以简洁的“表达式”形式实现轻量级逻辑,适合作为高阶函数参数或临时回调。
  • 变量作用域遵循LEGB规则,globalnonlocal关键字可修改变量的默认作用域行为,避免作用域混淆是代码健壮性的关键。

掌握这些函数特性,不仅能提升代码的简洁性与可读性,更能深入理解Python的底层逻辑,为编写高效、优雅的程序奠定基础。

你可能感兴趣的:(lesson17:Python函数之递归、匿名函数与变量作用域)