Python 函数

1. 函数基础:定义与抽象

函数是组织好的、可重复使用的代码块。在编程中,函数代表了一种抽象能力:正如数学中使用 \sum 表示求和,编程中使用函数来封装复杂的逻辑。

1.1 定义语法

使用 def 关键字定义函数,推荐包含 文档字符串 (Docstring)类型提示 (Type Hints)

import math

def area_of_circle(radius: float) -> float:
    """计算圆的面积。
    参数: radius (float) - 半径
    返回: float - 面积
    """
    return math.pi * radius ** 2

1.2 函数的返回值

  • 单一返回值:使用 return 返回结果。
  • 多个返回值:使用 return x, y(本质上是返回一个 tuple)。
  • 空函数:使用 pass 作为占位符。若无 return,默认返回 None

2. 函数调用

定义函数后,它只是静静地待在内存里,直到你调用它。

2.1 基本调用方法

要使用上述函数,你需要写出函数名并用括号包裹参数:

# 1. 直接传递数值
result = area_of_circle(5.0) 
print(result)

# 2. 传递变量
r = 10.5
s = area_of_circle(r)
print(f"半径为{r}的面积是: {s:.2f}")

# 3. 直接在表达式中使用
total_area = area_of_circle(2) + area_of_circle(3)

2.2 调用时的参数传递机制

  • 位置调用power(5, 3) 严格按照定义顺序赋值。
  • 关键字调用area_of_circle(radius=5.0)。这种方式更清晰,且在参数多时允许乱序。
  • 内存本质:Python 采用 “对象引用传递”
  • 传入不可变对象(数字、字符串):函数内部修改不会影响外部。
  • 传入可变对象(列表、字典):函数内部的修改(如 .append()会同步改变外部变量

2.3 动态调用(参数解包)

如果你有一个列表或字典,可以使用 *** 快速调用:

params = [5.0]
print(area_of_circle(*params)) # 解包列表

def greet(name, age): print(f"{name}: {age}")
data = {"name": "Alice", "age": 25}
greet(**data) # 解包字典

3. 深入理解函数参数

Python 的参数系统提供了极大的灵活性,但定义顺序必须严格遵守: 位置参数 \rightarrow 默认参数 \rightarrow 可变参数 \rightarrow 命名关键字参数 \rightarrow 关键字参数

参数类型语法特点
位置参数def f(a, b)调用时必选,按顺序传入。
默认参数def f(a, b=2)必须指向不可变对象(如 None、整数、字符串)。
可变参数def f(*args)接收一个 tuple,允许传入任意个位置参数。
命名关键字参数def f(*, city)强制必须使用 city='Beijing' 这种显式键值对传入。
关键字参数def f(**kw)接收一个 dict,扩展函数的灵活性。

4. 递归函数:深度解析与进阶

递归是指函数调用自身。它能将复杂问题分解为规模更小的子问题。

4.1 经典案例:阶乘与汉诺塔

阶乘的递归定义为 n!=n×(n1)!n! = n \times (n-1)!

def factorial(n):
    if n == 1: return 1  # 终止条件
    return n * factorial(n - 1)

4.2 递归最佳实践 (关键)

  1. 明确定义基本情况:必须有明确的递归终止条件(如 if n == 1)。
  2. 向基本情况靠近:确保每次递归调用都在缩小问题范围,防止死循环。
  3. 合理设置深度限制:Python 默认限制为 1000 层(可用 sys.setrecursionlimit 修改,但不推荐)。
  4. 性能权衡:对于性能极其敏感的场景,考虑用迭代(循环)替代递归,以节省栈空间。

4.3 现代 Python 优化:记忆化缓存

在 Python 3.9+ 中,使用 functools 可以显著提升递归效率,避免重复计算(如斐波那契数列)。

from functools import lru_cache, cache

@cache  # 自动缓存计算结果
def fibonacci(n):
    if n < 2: return n
    return fibonacci(n-1) + fibonacci(n-2)

5. 最佳实践与规范

  1. 单一职责原则:每个函数只做一件事。
  2. 命名规范:使用小写字母和下划线,如 calculate_total_price
  3. 参数验证:在函数内部使用 isinstance() 检查输入合法性并抛出 TypeError
  4. 避免副作用:尽量不要在函数内部修改全局变量或外部传入的可变对象。

6. 综合示例:一元二次方程求解

结合类型提示、参数验证和数学模块,求解 ax2+bx+c=0ax^2 + bx + c = 0

import math

def quadratic(a: float, b: float, c: float) -> tuple[float, float]:
    """求解一元二次方程"""
    if not all(isinstance(x, (int, float)) for x in [a, b, c]):
        raise TypeError("参数必须是数字")
        
    delta = b**2 - 4*a*c
    if delta < 0:
        raise ValueError("方程无实数解")
        
    sqrt_delta = math.sqrt(delta)
    x1 = (-b + sqrt_delta) / (2*a)
    x2 = (-b - sqrt_delta) / (2*a)
    return x1, x2

# 调用测试
print(f"解为: {quadratic(2, 3, 1)}")  # 输出: (-0.5, -1.0)

7. 总结

递归是解决复杂逻辑的强大武器,但在 Python 中使用时需注意其内存限制。通过结合默认参数简化调用、关键字参数增加扩展性,以及缓存机制优化性能,你可以构建出既优雅又高效的 Python 程序。