发布于2026-07-21 阅读(0)
扫一扫,手机访问
先说说递归的本质——它本质上就是一个函数自己调用自己,就像数学归纳法一样,把大问题拆成小问题,直到小到不能再小。这种编程技巧在Python中非常实用,但很多人一看到递归就头疼,其实它没那么神秘。

递归,说白了就是一个函数在内部调用自己。听起来有点绕,但它的核心思想其实很简单——把大问题不断拆解,直到拆成可以直接解决的小问题。一个递归函数通常有两个关键部分:一是终止条件,告诉程序什么时候停下;二是递归步骤,让问题一步步缩小。没有终止条件,函数就会无限循环,最终撑爆栈内存。
想想俄罗斯套娃,打开一个,里面还有更小的,一直开到最小的那个为止,然后再一层层合回去。递归的逻辑跟这个如出一辙。
光讲概念不够,咱们直接上代码,看几个经典例子。
阶乘的定义本身就是递归的:n! = n × (n-1)!,0! = 1。来看代码:
def factorial(n):
"""递归计算阶乘"""
# 终止条件:0! = 1
if n == 0:
return 1
# 递归步骤:n! = n × (n-1)!
return n * factorial(n - 1)
print(factorial(5)) # 输出:120
你想想看,factorial(5)是怎么算的?它先变成5×factorial(4),然后4×factorial(3)……一路拆解到factorial(0)=1,然后再逐层往回运算,最终得到120。这个过程就像搭积木,一层层拆,再一层层搭回来。
斐波那契数列的定义更直接:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)。代码写出来非常简洁:
def fibonacci(n):
"""递归计算第 n 个斐波那契数"""
if n <= 0:
return 0
if n == 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10)) # 输出:55
不过这里要提醒一句,这种写法虽然简单,但计算效率很低——因为很多子问题被重复计算了。比如fibonacci(5)会重复调用fibonacci(3)两次,指数级增长,数据量大时非常慢。后面会讲到如何优化。
当数据结构天生就是递归的,比如嵌套列表,用递归处理最自然:
def flatten(nested_list):
"""递归展平嵌套列表"""
result = []
for item in nested_list:
if isinstance(item, list):
# 如果是列表,递归展平
result.extend(flatten(item))
else:
result.append(item)
return result
data = [1, [2, [3, 4], 5], 6, [7, 8]]
print(flatten(data)) # 输出:[1, 2, 3, 4, 5, 6, 7, 8]
看,代码逻辑清晰,遇到列表就继续拆,直到拆出单个元素为止。这种写法比用循环栈要简洁得多。
递归的代码确实简洁,这一点毋庸置疑。比如汉诺塔问题,递归解法几行代码搞定,迭代版本复杂得多。而且递归符合人类思维,很多问题天生就是递归的——树遍历、分治算法,用递归写出来跟问题定义几乎一模一样,可读性极强。对于嵌套结构,递归几乎是必选方案。
但是,递归的代价也不小。每次函数调用都要在栈上分配空间,深度一大,内存消耗就上去了。Python默认递归深度大约1000层,超过就会抛出RecursionError。虽然可以通过sys.setrecursionlimit()调整,但那只是治标不治本。另外,像斐波那契那样的递归,存在大量重复计算,效率很差。调试起来也比较麻烦,多层嵌套让人头疼。
递归适合问题本身有递归定义、数据结构是树或图、需要回溯搜索(如八皇后)或分治算法(如归并排序)的场景。对于简单的线性问题,迭代通常更合适。
在Python中,函数不仅是组织代码的基本单元,更是"一等公民"。这意味着函数可以像整数、字符串一样被操作。这一点很重要,因为它是理解Python高级特性的基础。
Python中万物皆对象,函数也不例外。每个函数都是function类的实例,拥有自己的属性和方法:
def greet(name):
"""一个简单的问候函数"""
return f"你好,{name}!"
# 函数是一个对象
print(type(greet)) # 输出:
print(isinstance(greet, object)) # 输出:True
print(greet.__name__) # 输出:'greet'
print(greet.__doc__) # 输出:'一个简单的问候函数'
既然函数是对象,就可以像普通对象一样动态添加属性。这在需要为函数附加额外信息时非常实用:
def process_data(data):
"""处理数据"""
process_data.call_count += 1
return [x * 2 for x in data]
# 动态添加属性
process_data.call_count = 0
process_data.author = "张三"
process_data.version = "1.0.0"
print(process_data([1, 2, 3])) # 输出:[2, 4, 6]
print(process_data.call_count) # 输出:1
print(process_data.author) # 输出:张三
process_data([4, 5, 6])
print(process_data.call_count) # 输出:2
这种特性在实现装饰器和缓存机制时特别有用,可以为函数附加缓存字典、元数据或配置项。
函数名本质上只是一个指向函数对象的引用,所以可以赋值给另一个变量,通过新变量名来调用:
def say_hello(name):
return f"Hello, {name}!"
# 将函数赋值给变量(注意:不要加括号,加括号表示调用)
greeting = say_hello
welcome = say_hello
print(greeting("Alice")) # 输出:Hello, Alice!
print(welcome("Bob")) # 输出:Hello, Bob!
print(greeting is say_hello) # 输出:True,指向同一个对象
这样一来,我们可以灵活地为函数起别名,或者在运行时根据条件选择不同的函数实现。
能够接受其他函数作为参数,或者将函数作为返回值返回的函数,称为高阶函数。这是函数式编程的核心思想:
def apply_twice(func, value):
"""将函数应用到值上两次"""
return func(func(value))
def add_three(x):
return x + 3
def multiply_two(x):
return x * 2
print(apply_twice(add_three, 5)) # 输出:11(5+3=8, 8+3=11)
print(apply_twice(multiply_two, 3)) # 输出:12(3×2=6, 6×2=12)
这种模式让代码具有极高的灵活性和复用性——我们可以把行为(函数)作为参数传入,而不需要为每种场景写一套新代码。常见应用包括回调函数、事件处理器和排序时的key参数。
函数可以在内部定义另一个函数并返回它,这种技术通常用于创建闭包和函数工厂:
def make_multiplier(factor):
"""返回一个将输入乘以 factor 的函数"""
def multiplier(x):
return x * factor
return multiplier # 返回内部函数
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(10)) # 输出:20
print(triple(10)) # 输出:30
这里make_multiplier就像一个"函数工厂",根据不同的参数生产出行为不同的函数。multiplier函数记住了外层函数中的变量factor,即使在外层函数已经返回之后仍然可以访问——这就是闭包的机制。
匿名函数使用lambda关键字定义,语法为lambda 参数: 表达式。它不需要函数名,只能包含单个表达式,适用于简单的、一次性的操作:
# 普通函数
def square(x):
return x * x
# 等价的匿名函数
square_lambda = lambda x: x * x
print(square(5)) # 输出:25
print(square_lambda(5)) # 输出:25
# 匿名函数最常见的场景:作为高阶函数的参数
numbers = [1, 2, 3, 4, 5]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # 输出:[2, 4]
使用建议:lambda适合简短的单行逻辑。如果逻辑复杂、需要多行代码或包含循环/异常处理,应使用普通命名函数,以保证可读性和可维护性。
高阶函数是指至少满足以下一个条件的函数:接受一个或多个函数作为参数,或者返回一个函数作为结果。高阶函数是函数式编程的基石,它让代码更抽象、更模块化。前面apply_twice和make_multiplier都是高阶函数的例子。接下来,我们重点看看Python内置的几个高阶函数。
Python提供了四个非常实用的内置高阶函数:map、reduce、filter和sorted。它们配合lambda表达式,可以写出简洁而强大的数据处理流水线。
map(func, iterable)将函数func应用到可迭代对象的每一个元素上,返回一个迭代器,包含所有元素经过函数处理后的结果:
# 将列表中的每个数字平方 numbers = [1, 2, 3, 4, 5] squared = list(map(lambda x: x ** 2, numbers)) print(squared) # 输出:[1, 4, 9, 16, 25] # 结合命名函数,将温度从摄氏度转为华氏度 celsius = [0, 10, 20, 30, 40] fahrenheit = list(map(lambda c: c * 9/5 + 32, celsius)) print(fahrenheit) # 输出:[32.0, 50.0, 68.0, 86.0, 104.0] # map 可以接受多个可迭代对象,func 需要接受对应数量的参数 a = [1, 2, 3] b = [4, 5, 6] sums = list(map(lambda x, y: x + y, a, b)) print(sums) # 输出:[5, 7, 9]
适用场景:对序列中每个元素执行相同的转换操作,如类型转换、数学运算、格式规范化等。
filter(func, iterable)使用函数func对可迭代对象的每个元素进行筛选,保留func返回True的元素,返回一个迭代器:
# 筛选出偶数 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] evens = list(filter(lambda x: x % 2 == 0, numbers)) print(evens) # 输出:[2, 4, 6, 8, 10] # 筛选出长度大于 3 的字符串 words = ["hi", "hello", "sun", "python", "go", "world"] long_words = list(filter(lambda w: len(w) > 3, words)) print(long_words) # 输出:['hello', 'python', 'world']
适用场景:根据条件过滤数据,如去除空值、筛选符合条件的记录等。
reduce(func, iterable[, initial])位于functools模块中,它将一个接受两个参数的函数累积地应用到序列的元素上,将序列"归约"为一个单一值。工作方式是:先对前两个元素执行函数,得到结果后再与第三个元素执行函数,以此类推:
from functools import reduce # 计算列表所有元素的乘积 numbers = [1, 2, 3, 4, 5] product = reduce(lambda x, y: x * y, numbers) print(product) # 输出:120(即 1×2×3×4×5) # 找出列表中的最大值 values = [23, 45, 12, 67, 34, 89, 5] max_value = reduce(lambda x, y: x if x > y else y, values) print(max_value) # 输出:89 # 使用 initial 参数指定初始值 numbers = [1, 2, 3] total = reduce(lambda x, y: x + y, numbers, 10) print(total) # 输出:16(即 10+1+2+3)
执行过程详解(以product为例):第一步:lambda(1, 2) → 2,第二步:lambda(2, 3) → 6,第三步:lambda(6, 4) → 24,第四步:lambda(24, 5) → 120。
适用场景:累积计算(求和、求积)、合并数据、构建嵌套结构等需要将序列归约为单一值的操作。
sorted(iterable, key=None, reverse=False)返回一个新的排序后的列表。它虽然不是严格意义上的"接收函数作为参数"的高阶函数形式,但其key参数接受一个函数,用于指定排序的依据——这使它具备了高阶函数的特性:
# 按绝对值排序
numbers = [-5, 3, -1, 4, -2]
sorted_by_abs = sorted(numbers, key=lambda x: abs(x))
print(sorted_by_abs) # 输出:[-1, -2, 3, 4, -5]
# 按字符串长度排序
words = ["python", "go", "ja va", "c", "rust", "ja vascript"]
sorted_by_len = sorted(words, key=lambda w: len(w))
print(sorted_by_len) # 输出:['c', 'go', 'ja va', 'rust', 'python', 'ja vascript']
# 多条件排序:先按成绩降序,再按姓名升序
students = [
{"name": "张三", "score": 85},
{"name": "李四", "score": 92},
{"name": "王五", "score": 85},
{"name": "赵六", "score": 78},
]
sorted_students = sorted(students, key=lambda s: (-s["score"], s["name"]))
print(sorted_students)
# 输出:[{'name': '李四', 'score': 92}, {'name': '张三', 'score': 85},
# {'name': '王五', 'score': 85}, {'name': '赵六', 'score': 78}]
sorted和列表的list.sort()方法功能相似,但sorted返回新列表且适用于任意可迭代对象,而sort在原地修改列表。
适用场景:对复杂数据结构按自定义规则排序,如按对象属性、按计算结果或按多个条件排序。
本文从递归的基础概念出发,通过阶乘、斐波那契和嵌套列表展平等案例展示了递归的适用场景和运作原理,并客观分析了递归的优缺点。接着深入探讨了Python中函数作为"一等公民"的多种特性——函数是对象、可以动态添加属性、可以赋值给变量、可以作为参数和返回值——这些特性构成了Python函数式编程和装饰器等高级特性的基础。最后,我们系统介绍了匿名函数lambda以及map、reduce、filter、sorted四个内置高阶函数,通过丰富的代码示例展示了它们在实际开发中的用法。
掌握递归和高阶函数,不仅能让你的代码更加简洁优雅,更能帮助你从更高的抽象层次思考和解决问题。建议读者在理解这些概念的基础上,多动手实践,将递归思维和函数式编程风格融入到日常编码中。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8