Python 函数与编程范式

Python 的函数是一等公民——可以赋值给变量、作为参数传递、从其他函数返回。这种设计使 Python 天然支持函数式编程,同时保持面向对象和过程式的灵活性。


函数基础

定义函数

def 函数名(参数列表):
    """文档字符串"""
    函数体
 
def fib(n):
    """输出限定数值内的斐波那契数列"""
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a + b

return 语句退出函数并返回值。未显式 return 时返回 None

参数传递

Python 中一切都是对象。参数传递的行为取决于对象是否可变:

  • 不可变对象(int、str、tuple):函数内部重新赋值不影响外部——类似”值传递”
  • 可变对象(list、dict、set):函数内部修改会影响外部——类似”引用传递”
def modify(x, lst):
    x = x + 1       # 新对象,外部不受影响
    lst.append(4)   # 原地修改,外部受影响
 
a = 10
b = [1, 2, 3]
modify(a, b)
# a 还是 10,b 变成了 [1, 2, 3, 4]

参数系统

默认值参数

def add(a=0, b=0, c=0):
    return a + b + c
 
add(1, 2)      # 3
add(1, 2, 3)   # 6

可变参数 *args

接收任意数量的位置参数,打包为元组:

def add2(*args):
    total = 0
    for val in args:
        total += val
    return total
 
add2(1, 2, 3)  # 6

关键字参数 **kwargs

接收任意数量的键值参数,打包为字典:

def add2(**kwargs):
    print(kwargs)
 
add2(name="halo")  # {'name': 'halo'}

仅限位置参数 /

/ 之前的参数必须按位置传递:

def pos_only_arg(arg, /):
    print(arg)
 
pos_only_arg(1)         # 正确
pos_only_arg(arg=1)     # TypeError

仅限关键字参数 *

* 之后的参数必须按键值传递:

def kwd_only_arg(*, arg):
    print(arg)
 
kwd_only_arg(arg=1)     # 正确
kwd_only_arg(1)         # TypeError

参数顺序

def f(pos_only, /, standard, *, kwd_only):
    pass
 
# 调用示例
f(1, 2, kwd_only=3)
f(1, standard=2, kwd_only=3)

作用域:global 与 nonlocal

  • 全局变量:函数体外声明,整个程序可见
  • 局部变量:函数体内声明,仅函数内部可见
x = 10  # 全局
 
def func():
    y = 5  # 局部
    global x
    x = 20  # 修改全局
 
func()
print(x)  # 20

nonlocal 用于嵌套函数中修改外层(但不是全局)变量:

def outer():
    x = 10
    def inner():
        nonlocal x
        x = 20
    inner()
    print(x)  # 20

yield 与生成器

包含 yield 的函数是生成器函数——调用时返回迭代器,惰性产生值,省内存:

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b
 
# 取前 10 个斐波那契数
for _, val in zip(range(10), fibonacci()):
    print(val)

每次 yield 暂停函数并保存状态,下次迭代从暂停处继续。


lambda 匿名函数

单表达式匿名函数,语法糖:

double = lambda x: x * 2
double(5)  # 10
 
# 等价于
def double(x):
    return x * 2

常用作 mapfiltersorted 的 key 参数。


函数式编程

函数式编程(FP)用数学函数方式构建计算——纯函数、不可变数据、函数组合。

核心工具

map — 对每个元素应用函数:

squared = list(map(lambda x: x**2, [1, 2, 3, 4]))
# [1, 4, 9, 16]

filter — 按条件筛选:

evens = list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5]))
# [2, 4]

reduce — 累积计算:

from functools import reduce
product = reduce(lambda x, y: x * y, [1, 2, 3, 4])
# 24

partial — 固定部分参数:

from functools import partial
 
def multiply(x, y):
    return x * y
 
double = partial(multiply, 2)
double(4)  # 8

高阶函数

接受或返回函数的函数:

def greet(type_):
    if type_ == 'hello':
        return lambda name: "Hello, " + name
    return lambda name: "Hi, " + name
 
greeting = greet('hello')
greeting('Alice')  # 'Hello, Alice'

纯函数与不可变性

纯函数满足两个条件:

  1. 相同输入必定产生相同输出
  2. 没有副作用(不修改外部状态、不 I/O)
# 纯函数
def add(x, y):
    return x + y
 
# 不纯——修改了外部变量
counter = 0
def increment():
    global counter
    counter += 1
    return counter

FP 风格中数据是不可变的——不修改原数据,而是产生新副本:

# 错误(FP 视角):原地修改
lst = [1, 2, 3]
lst.append(4)
 
# 正确:返回新列表
new_lst = lst + [4]

装饰器

装饰器在不修改原函数代码的情况下增加功能——本质是接受函数、返回函数的高阶函数。

def my_decorator(func):
    def wrapper():
        print("调用前")
        func()
        print("调用后")
    return wrapper
 
@my_decorator
def say_hello():
    print("Hello!")
 
say_hello()
# 调用前
# Hello!
# 调用后

带参数的装饰器:

def repeat(num):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator
 
@repeat(num=4)
def greet(name):
    print(f"Hello, {name}")
 
greet("Alice")  # 打印四次

装饰器常见用途:日志、计时、权限检查、缓存(functools.lru_cache)。


闭包

闭包是能记住创建时所在作用域变量的函数:

def outer_function(x):
    def inner_function(y):
        return x + y      # inner 记住了 x
    return inner_function
 
closure = outer_function(10)
closure(5)  # 15

递归

函数调用自身解决更小的问题实例:

def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)
 
factorial(5)  # 120

递归 vs 迭代:

维度递归迭代
直观性对树遍历等问题更自然对数组/链表更自然
内存每次调用占用栈空间通常更省内存
风险可能栈溢出无栈溢出风险
效率大量递归不如迭代简单循环更高效

Python 默认递归深度限制约 1000,可调整但不推荐。能用迭代时优先迭代。


关联