python从入门到精通-第5章: 函数式编程 — Python的函数式风格

第5章: 函数式编程 --- Python的函数式风格

Java/Kotlin 开发者习惯了 Stream API 和 lambda 表达式,但 Python 的函数式编程走的是完全不同的路。Python 没有函数类型签名、没有受检异常、lambda 只能写单行------但 Python 有一等函数、装饰器、生成器、functoolsitertools 这些 JVM 世界不存在的利器。装饰器是 Python 最强大的元编程手段,生成器是惰性求值的底层原语,itertools 提供了 Java Stream 望尘莫及的组合能力。 本章从一等函数出发,逐步拆解 Python 函数式编程的全部核心。


5.1 一等函数: 函数是对象

Java/Kotlin 对比

java 复制代码
// Java: Lambda 是函数式接口的实例,不是一等对象
// 你不能把一个方法本身传来传去,必须包装成函数式接口
import java.util.function.*;

Function<Integer, Integer> doubleIt = x -> x * 2;
// doubleIt 是一个对象,但类型是 Function<Integer,Integer>,不是"函数"
// 你不能直接把 System.out::println 存到 Map<String, ?> 里当通用函数用

// 方法引用只是语法糖,底层还是函数式接口
Consumer<String> printer = System.out::println;
kotlin 复制代码
// Kotlin: 函数类型是一等公民,和 Python 最接近
val doubleIt: (Int) -> Int = { x -> x * 2 }
val greet: (String) -> Unit = { println("Hi, $it") }

// 可以存入集合
val ops = mapOf(
    "double" to { x: Int -> x * 2 },
    "square" to { x: Int -> x * x }
)

Python 实现

python 复制代码
# === 函数就是对象,类型是 function ===
def greet(name: str) -> str:
    return f"Hello, {name}"

print(type(greet))        # <class 'function'>
print(greet.__name__)     # 'greet'
print(id(greet))          # 内存地址


# === 1. 赋值给变量 ===
say_hi = greet            # 没有括号!不是调用,是引用
print(say_hi("Alice"))    # Hello, Alice


# === 2. 存入数据结构 ===
ops = {
    "double": lambda x: x * 2,
    "square": lambda x: x ** 2,
    "negate": lambda x: -x,
}

print(ops["double"](5))   # 10
print(ops["square"](3))   # 9

# 函数列表,按顺序执行
pipeline = [
    lambda x: x.strip(),
    lambda x: x.lower(),
    lambda x: x.replace(" ", "-"),
]

text = "  Hello World  "
result = text
for fn in pipeline:
    result = fn(result)
print(result)  # 'hello-world'


# === 3. 作为参数传递 ===
def apply(fn, value):
    return fn(value)

print(apply(lambda x: x * 2, 5))    # 10
print(apply(str.upper, "hello"))    # HELLO


# === 4. 作为返回值 ===
def power(exp):
    """返回一个求幂函数"""
    return lambda base: base ** exp

square = power(2)
cube = power(3)
print(square(5))   # 25
print(cube(3))     # 27


# === 5. 函数属性:函数对象可以携带任意数据 ===
def important_func():
    """一个重要的函数"""
    pass

important_func.version = "1.0"
important_func.author = "Alice"
important_func.tags = ["math", "utils"]

print(important_func.version)  # 1.0
print(important_func.tags)     # ['math', 'utils']

# 这在 Java 中做不到------方法不能携带自定义属性


# === 6. 函数比较 ===
def foo():
    pass

bar = foo
print(foo is bar)          # True --- 同一个对象
print(foo == bar)          # True

def baz():
    pass

print(foo == baz)          # False --- 不同函数对象,即使代码相同

核心差异

特性 Java Kotlin Python
函数是一等对象 否(通过函数式接口模拟)
函数可携带属性
函数类型 Function<T,R> 等接口 (T) -> R Callable 协议
lambda 限制 必须匹配函数式接口 只能单行表达式

常见陷阱

python 复制代码
# 陷阱:lambda 的延迟绑定
funcs = [lambda: i for i in range(5)]
print([f() for f in funcs])  # [4, 4, 4, 4, 4] --- 全部是 4!

# 原因:lambda 捕获的是变量 i 的引用,不是值
# 循环结束后 i = 4,所有 lambda 共享同一个 i

# 修复:用默认参数捕获值
funcs = [lambda i=i: i for i in range(5)]
print([f() for f in funcs])  # [0, 1, 2, 3, 4]

何时使用

  • 需要把行为作为数据传递时(策略模式、回调)
  • 构建函数管道/处理链时
  • 需要给函数附加元数据时(Python 独有优势)

5.2 高阶函数: map, filter, sorted

Java/Kotlin 对比

java 复制代码
// Java Stream API --- 链式调用,惰性求值
import java.util.*;
import java.util.stream.*;

List<Integer> nums = List.of(1, 2, 3, 4, 5);
List<Integer> result = nums.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * n)
    .collect(Collectors.toList());
// [4, 16]

// sorted
List<String> names = List.of("Charlie", "Alice", "Bob");
List<String> sorted = names.stream()
    .sorted(Comparator.comparingInt(String::length))
    .collect(Collectors.toList());
// [Bob, Alice, Charlie]

// Java Stream 只能用一次!
Stream<Integer> s = nums.stream();
s.filter(n -> n > 2);  // 消费了
s.map(n -> n * 2);     // IllegalStateException: stream has already been operated upon
kotlin 复制代码
// Kotlin 集合函数 --- 直接在集合上调用,非惰性
val nums = listOf(1, 2, 3, 4, 5)
val result = nums.filter { it % 2 == 0 }.map { it * it }
// [4, 16]

// 惰性版本:asSequence()
val result2 = nums.asSequence()
    .filter { it % 2 == 0 }
    .map { it * it }
    .toList()

Python 实现

python 复制代码
# === map: 映射 ===
nums = [1, 2, 3, 4, 5]

# 返回迭代器(惰性),不是列表!
squares = map(lambda x: x ** 2, nums)
print(type(squares))          # <class 'map'>
print(list(squares))          # [1, 4, 9, 16, 25]

# 多参数 map
names = ["alice", "bob", "charlie"]
upper = map(str.upper, names)
print(list(upper))            # ['ALICE', 'BOB', 'CHARLIE']

# 多序列并行 map
a = [1, 2, 3]
b = [10, 20, 30]
print(list(map(lambda x, y: x + y, a, b)))  # [11, 22, 33]

# Pythonic 写法:列表推导式通常比 map 更清晰
squares = [x ** 2 for x in nums]
print(squares)  # [1, 4, 9, 16, 25]


# === filter: 过滤 ===
evens = filter(lambda x: x % 2 == 0, nums)
print(list(evens))            # [2, 4]

# Pythonic 写法
evens = [x for x in nums if x % 2 == 0]
print(evens)                  # [2, 4]


# === sorted: 排序(返回新列表,不修改原列表) ===
words = ["banana", "apple", "cherry", "date"]
print(sorted(words))                    # ['apple', 'banana', 'cherry', 'date']
print(sorted(words, key=len))           # ['date', 'apple', 'banana', 'cherry']
print(sorted(words, key=lambda w: w[-1]))  # ['apple', 'banana', 'date', 'cherry']

# 反向排序
print(sorted(words, reverse=True))      # ['date', 'cherry', 'banana', 'apple']

# 多级排序:先按长度,再按字母
data = [("alice", 30), ("bob", 25), ("charlie", 25)]
print(sorted(data, key=lambda x: (x[1], x[0])))
# [('bob', 25), ('charlie', 25), ('alice', 30)]

# 注意:list.sort() 是原地排序,sorted() 返回新列表
words_copy = words[:]
words_copy.sort(key=len)    # 原地修改
print(words_copy)           # ['date', 'apple', 'banana', 'cherry']


# === reduce: 归约 ===
from functools import reduce

nums = [1, 2, 3, 4, 5]
total = reduce(lambda acc, x: acc + x, nums)
print(total)                 # 15

# 带初始值(推荐,避免空序列报错)
total = reduce(lambda acc, x: acc + x, nums, 0)
print(total)                 # 15

empty_result = reduce(lambda acc, x: acc + x, [], 0)
print(empty_result)          # 0 --- 有初始值,空序列不报错

# Pythonic 写法:sum() 比 reduce 更清晰
print(sum(nums))             # 15


# === any / all: 短路求值 ===
nums = [1, 2, 3, 4, 5]
print(any(x > 10 for x in nums))   # False
print(any(x > 3 for x in nums))    # True
print(all(x > 0 for x in nums))    # True
print(all(x > 2 for x in nums))    # False


# === zip: 并行迭代 ===
names = ["alice", "bob", "charlie"]
scores = [95, 87, 92]
print(list(zip(names, scores)))
# [('alice', 95), ('bob', 87), ('charlie', 92)]

# 字典推导
print(dict(zip(names, scores)))
# {'alice': 95, 'bob': 87, 'charlie': 92}

# 不等长时,以最短的为准(和 Kotlin zip 一致)
print(list(zip([1, 2, 3], [10, 20])))  # [(1, 10), (2, 20)]


# === enumerate: 带索引迭代 ===
# Java: 没有直接等价物,通常用 IntStream.range
# Kotlin: withIndex()
for i, name in enumerate(names):
    print(f"{i}: {name}")
# 0: alice
# 1: bob
# 2: charlie

# 指定起始索引
for i, name in enumerate(names, start=1):
    print(f"{i}: {name}")
# 1: alice
# 2: bob
# 3: charlie

核心差异

特性 Java Stream Kotlin Python
惰性求值 是(直到终端操作) 默认非惰性,asSequence() 惰性 map/filter 返回迭代器,惰性
可复用 否(只能消费一次) 集合可复用,Sequence 只能一次 迭代器只能消费一次,列表可复用
多参数 map mapToObj zip + map map 原生支持多序列
并行 .parallelStream() 协程/Flow concurrent.futures

常见陷阱

python 复制代码
# 陷阱:迭代器只能消费一次
m = map(lambda x: x * 2, [1, 2, 3])
print(list(m))  # [2, 4, 6]
print(list(m))  # [] --- 已经耗尽!

# 修复:需要多次使用时,转为列表
result = list(map(lambda x: x * 2, [1, 2, 3]))
print(result)   # [2, 4, 6]
print(result)   # [2, 4, 6]

# 陷阱:sorted() 返回新列表,不修改原列表
words = ["b", "a", "c"]
sorted(words)
print(words)    # ['b', 'a', 'c'] --- 没变!
words.sort()
print(words)    # ['a', 'b', 'c'] --- 原地排序才变

何时使用

  • map/filter:简单转换时可用,但列表推导式通常更 Pythonic
  • sorted:需要保持原列表不变时
  • any/all:条件检查,比手写循环清晰
  • zip/enumerate:并行迭代和带索引迭代,极其常用

5.3 闭包与 nonlocal

Java/Kotlin 对比

java 复制代码
// Java: lambda 只能捕获 effectively final 变量
int factor = 2;
// factor = 3;  // 取消注释就编译错误!
Function<Integer, Integer> multiplier = x -> x * factor;
// factor 必须是 effectively final

// Java lambda 不能修改捕获的变量
// 没有 nonlocal 等价物
kotlin 复制代码
// Kotlin: lambda 可以捕获可变变量(var)
var count = 0
val inc: () -> Unit = { count++ }
inc()
inc()
println(count)  // 2 --- 可以修改捕获的变量

Python 实现

python 复制代码
# === 闭包基础 ===
def make_multiplier(factor):
    """返回一个乘法函数,factor 被闭包捕获"""
    def multiply(x):
        return x * factor  # factor 来自外层作用域
    return multiply

double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5))   # 10
print(triple(5))   # 15

# 查看闭包捕获的变量
print(double.__closure__)      # (<cell at ...: int object at ...>,)
print(double.__closure__[0].cell_contents)  # 2


# === 闭包陷阱:循环变量绑定 ===
def make_funcs():
    funcs = []
    for i in range(3):
        def f():
            return i  # 捕获的是变量 i,不是值
        funcs.append(f)
    return funcs

funcs = make_funcs()
print([f() for f in funcs])  # [2, 2, 2] --- 全部是 2!

# 修复1:默认参数捕获值
def make_funcs_fixed():
    funcs = []
    for i in range(3):
        def f(i=i):  # 默认参数在定义时求值
            return i
        funcs.append(f)
    return funcs

print([f() for f in make_funcs_fixed()])  # [0, 1, 2]

# 修复2:用工厂函数创建独立作用域
def make_func(i):
    def f():
        return i
    return f

def make_funcs_fixed2():
    return [make_func(i) for i in range(3)]

print([f() for f in make_funcs_fixed2()])  # [0, 1, 2]


# === nonlocal: 修改闭包捕获的变量 ===
def make_counter():
    count = 0  # 闭包变量

    def increment():
        nonlocal count  # 声明使用外层作用域的 count
        count += 1
        return count

    def get_count():
        return count

    return increment, get_count

inc, get = make_counter()
print(inc())   # 1
print(inc())   # 2
print(inc())   # 3
print(get())   # 3

# 没有 nonlocal 会怎样?
def broken_counter():
    count = 0

    def increment():
        # count += 1  # UnboundLocalError: local variable 'count' referenced before assignment
        # 因为 += 意味着赋值,Python 把 count 当成局部变量
        pass

    return increment


# === nonlocal 实战:带状态的闭包 ===
def make_accumulator(initial=0):
    total = initial

    def add(value):
        nonlocal total
        total += value
        return total

    def reset():
        nonlocal total
        total = initial
        return total

    # 返回一个"对象"------用闭包模拟状态
    return {"add": add, "reset": reset, "get": lambda: total}

acc = make_accumulator(100)
print(acc["add"](10))    # 110
print(acc["add"](20))    # 130
print(acc["reset"]())    # 100
print(acc["add"](5))     # 105


# === global: 修改全局变量(慎用) ===
cache = {}

def memoize_global(key, value):
    global cache  # 声明使用全局变量
    cache[key] = value
    return value

memoize_global("a", 1)
print(cache)  # {'a': 1}

核心差异

特性 Java Kotlin Python
捕获 effectively final 强制 不强制 不强制
修改捕获的变量 不允许 允许 需要 nonlocal 声明
闭包变量绑定 值捕获 引用捕获 引用捕获
查看闭包变量 不支持 不支持 __closure__ 属性

常见陷阱

python 复制代码
# 陷阱1:赋值操作使变量变局部
x = 10
def foo():
    x = 20      # 创建了局部变量 x,不影响外层
    return x
print(foo())   # 20
print(x)       # 10 --- 外层没变

# 陷阱2:先读后写,UnboundLocalError
x = 10
def bar():
    # print(x)  # UnboundLocalError!
    x = 20      # 因为有赋值,Python 把 x 当局部变量
    return x

# 陷阱3:nonlocal 找的是最近外层函数作用域,不是全局
x = 10
def outer():
    x = 20
    def inner():
        nonlocal x  # 指向 outer 的 x,不是全局的 x
        x = 30
    inner()
    print(x)   # 30
outer()
print(x)       # 10 --- 全局的 x 没变

何时使用

  • 闭包:需要封装状态但不想创建完整类时(轻量级替代)
  • nonlocal:闭包中需要修改外层变量时
  • global:几乎不需要用,模块级状态用类管理更好

5.4 装饰器: Python 的杀手级特性

Java/Kotlin 对比

java 复制代码
// Java: 没有直接等价物
// 最接近的是注解处理器(编译期)和 AOP(运行期),但完全不同的机制
// Java 注解是被动标记,装饰器是主动代码变换

// Spring AOP --- 需要代理、切面、复杂的框架支持
@Around("execution(* com.example.service.*.*(..))")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("Before: " + pjp.getSignature());
    Object result = pjp.proceed();
    System.out.println("After");
    return result;
}
kotlin 复制代码
// Kotlin: 同样没有直接等价物
// 可以用高阶函数模拟,但语法不如装饰器优雅
fun <T> withLogging(fn: () -> T): T {
    println("Before")
    val result = fn()
    println("After")
    return result
}

Python 实现

python 复制代码
import functools
import time


# === 1. 基础装饰器:无参数 ===
def timer(func):
    """计时装饰器"""
    @functools.wraps(func)  # 保留原函数的元信息
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timer
def slow_add(a, b):
    time.sleep(0.1)
    return a + b

print(slow_add(1, 2))
# slow_add took 0.1001s
# 3

# @timer 等价于 slow_add = timer(slow_add)
# 装饰器的本质:函数 = 装饰器(原函数)


# === 2. 带参数的装饰器 ===
def retry(max_attempts=3, delay=1.0):
    """重试装饰器工厂"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"Attempt {attempt} failed: {e}")
                    if attempt == max_attempts:
                        raise
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(max_attempts=3, delay=0.1)
def unstable_api():
    import random
    if random.random() < 0.7:
        raise ConnectionError("Network error")
    return "success"

print(unstable_api())  # 可能重试几次后成功


# === 3. 类作为装饰器 ===
class CountCalls:
    """统计函数调用次数"""
    def __init__(self, func):
        self.func = func
        self.count = 0
        functools.update_wrapper(self, func)

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Call #{self.count} of {self.func.__name__}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello(name):
    return f"Hello, {name}"

print(say_hello("Alice"))  # Call #1 of say_hello
print(say_hello("Bob"))    # Call #2 of say_hello
print(say_hello.count)     # 2


# === 4. 装饰器堆叠 ===
def bold(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return f"**{func(*args, **kwargs)}**"
    return wrapper

def italic(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return f"*{func(*args, **kwargs)}*"
    return wrapper

# 执行顺序:从下往上装饰,从上往下执行
@bold
@italic
def greet(name):
    return f"Hello, {name}"

print(greet("Alice"))  # ***Hello, Alice*** --- italic 先执行,bold 后执行


# === 5. @functools.wraps 保留元信息 ===
def bad_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper  # 没有 @wraps!

@bad_decorator
def my_function():
    """This is my function."""
    pass

print(my_function.__name__)   # 'wrapper' --- 丢失了!
print(my_function.__doc__)    # None --- 丢失了!

# 用 @functools.wraps 修复
def good_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@good_decorator
def my_function2():
    """This is my function."""
    pass

print(my_function2.__name__)  # 'my_function2' --- 保留了!
print(my_function2.__doc__)   # 'This is my function.' --- 保留了!


# === 6. 装饰器工厂(带参数的类装饰器) ===
class Validate:
    """参数验证装饰器"""
    def __init__(self, **rules):
        self.rules = rules  # {参数名: (类型, 是否必填)}

    def __call__(self, func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 简单验证
            for param_name, (expected_type, required) in self.rules.items():
                value = kwargs.get(param_name)
                if required and value is None:
                    raise ValueError(f"Missing required parameter: {param_name}")
                if value is not None and not isinstance(value, expected_type):
                    raise TypeError(
                        f"Parameter {param_name} must be {expected_type}, got {type(value)}"
                    )
            return func(*args, **kwargs)
        return wrapper

@Validate(name=(str, True), age=(int, False))
def create_user(name, age=None):
    return {"name": name, "age": age}

print(create_user(name="Alice", age=30))  # {'name': 'Alice', 'age': 30}
# create_user(name="Alice", age="30")    # TypeError!
# create_user(age=30)                    # ValueError!


# === 7. 常用内置装饰器 ===

# @staticmethod: 静态方法,不接收 self/cls
class MathUtils:
    @staticmethod
    def add(a, b):
        return a + b

print(MathUtils.add(1, 2))  # 3

# @classmethod: 类方法,接收 cls 而非 self
class Database:
    _instance = None

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    @classmethod
    def reset(cls):
        cls._instance = None

db1 = Database.get_instance()
db2 = Database.get_instance()
print(db1 is db2)  # True --- 单例

# @dataclass: 自动生成 __init__, __repr__, __eq__
from dataclasses import dataclass, field

@dataclass
class Point:
    x: float
    y: float
    label: str = "origin"

    def distance_to(self, other):
        return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5

p1 = Point(3, 4)
p2 = Point(0, 0, "center")
print(p1)              # Point(x=3.0, y=4.0, label='origin')
print(p1 == Point(3, 4))  # True --- 自动生成 __eq__
print(p1.distance_to(p2))  # 5.0

# @dataclass 高级用法
@dataclass
class Order:
    items: list[str] = field(default_factory=list)
    total: float = 0.0

order = Order(items=["apple", "banana"])
print(order)  # Order(items=['apple', 'banana'], total=0.0)

核心差异

特性 Java 注解 Python 装饰器
执行时机 编译期/运行期(被动) 定义时立即执行(主动)
代码变换 需要注解处理器/AOP 直接修改函数对象
参数化 注解属性 装饰器工厂
组合 多注解可叠加 可堆叠,有执行顺序
元信息保留 反射 API @functools.wraps

常见陷阱

python 复制代码
# 陷阱1:装饰器返回的对象必须可调用
def broken_decorator(func):
    return None  # 不是可调用对象!

@broken_decorator
def foo():
    pass

# foo()  # TypeError: 'NoneType' object is not callable

# 陷阱2:带参数装饰器的三层嵌套容易搞混
# 正确结构:外层接收参数 → 中层接收函数 → 内层是实际包装
def with_param(arg):
    def decorator(func):        # 接收被装饰的函数
        def wrapper(*a, **kw):  # 实际执行
            return func(*a, **kw)
        return wrapper
    return decorator             # 返回装饰器

# 陷阱3:类装饰器不会自动继承被装饰类的行为
# 如果用类装饰器装饰类,需要实现 __init_subclass__ 或手动处理

何时使用

  • 日志、计时、缓存、权限检查:横切关注点
  • 注册机制:@app.route@pytest.fixture
  • 参数验证:输入校验
  • 单例/缓存:设计模式
  • 原则:装饰器应该透明,被装饰函数的行为不应被意外改变

5.5 functools: partial, lru_cache, singledispatch

Java/Kotlin 对比

java 复制代码
// Java: 没有直接等价物
// 偏函数:手动创建新方法或用方法引用
// 缓存:Guava Cache, Caffeine 等
// 泛型函数:方法重载(编译期分派)
kotlin 复制代码
// Kotlin: 偏函数用 :: 部分应用
fun power(base: Int, exp: Int): Int = Math.pow(base.toDouble(), exp.toDouble()).toInt()
val square = { x: Int -> power(x, 2) }  // 手动偏函数

Python 实现

python 复制代码
import functools
from functools import partial, lru_cache, singledispatch, total_ordering, wraps


# === 1. functools.partial: 偏函数 ===
# 固定函数的部分参数,返回新函数

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)
cube = partial(power, exp=3)
print(square(5))   # 25
print(cube(3))     # 27

# 固定位置参数
double = partial(power, 2)
print(double(10))  # 1024 --- 2^10

# 实际应用:固定 API 调用的公共参数
import urllib.request

# 假设有一个请求函数
def fetch(url, timeout, headers):
    return f"Fetching {url} with timeout={timeout}"

# 固定 timeout 和 headers
safe_fetch = partial(fetch, timeout=30, headers={"User-Agent": "MyApp"})
print(safe_fetch("https://example.com"))
# Fetching https://example.com with timeout=30

# partial 对象的属性
print(square.func)       # <function power>
print(square.args)       # ()
print(square.keywords)   # {'exp': 2}


# === 2. @lru_cache: 缓存(Python 杀手级特性) ===
# 自动缓存函数返回值,相同参数直接返回缓存结果

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# 没有缓存:O(2^n) 指数级
# 有缓存:O(n) 线性级
print(fibonacci(100))  # 354224848179261915075 --- 瞬间完成
print(fibonacci(100))  # 第二次直接从缓存读取

# 查看缓存状态
print(fibonacci.cache_info())
# CacheInfo(hits=100, misses=101, maxsize=128, currsize=101)

# 清除缓存
fibonacci.cache_clear()


# 无限缓存
@lru_cache(maxsize=None)
def expensive_computation(x):
    print(f"Computing for {x}...")
    return x * x

print(expensive_computation(5))  # Computing for 5... → 25
print(expensive_computation(5))  # 25 --- 直接返回,没有打印


# 缓存实战:HTTP 请求缓存
@lru_cache(maxsize=1000)
def get_user(user_id):
    """模拟数据库查询"""
    print(f"Querying DB for user {user_id}")
    return {"id": user_id, "name": f"User{user_id}"}

print(get_user(1))  # Querying DB for user 1 → {'id': 1, 'name': 'User1'}
print(get_user(1))  # {'id': 1, 'name': 'User1'} --- 缓存命中
print(get_user(2))  # Querying DB for user 2 → {'id': 2, 'name': 'User2'}


# === 3. @functools.singledispatch: 泛型函数 ===
# 根据第一个参数的类型分派到不同的实现

@singledispatch
def process(data):
    raise NotImplementedError(f"Cannot process {type(data)}")

@process.register(int)
def _(data):
    return f"Integer: {data * 2}"

@process.register(str)
def _(data):
    return f"String: {data.upper()}"

@process.register(list)
def _(data):
    return f"List: {len(data)} items"

@process.register(dict)
def _(data):
    return f"Dict: {len(data)} keys"

print(process(42))       # Integer: 84
print(process("hello"))  # String: HELLO
print(process([1, 2]))   # List: 2 items
print(process({"a": 1})) # Dict: 1 keys
# process(3.14)          # NotImplementedError

# 注册类型检查:支持 isinstance 语义
@process.register(float)
@process.register(int)   # 可以注册多个类型
def _(data):
    return f"Number: {data}"


# === 4. @functools.total_ordering ===
# 只需实现 __eq__ 和一个比较方法,自动生成其余

@total_ordering
class Version:
    def __init__(self, major, minor, patch):
        self.major = major
        self.minor = minor
        self.patch = patch

    def __eq__(self, other):
        return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)

    def __lt__(self, other):
        return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)

    def __repr__(self):
        return f"Version({self.major}.{self.minor}.{self.patch})"

v1 = Version(1, 0, 0)
v2 = Version(2, 0, 0)
v3 = Version(1, 0, 0)

print(v1 < v2)   # True
print(v2 > v1)   # True --- total_ordering 自动生成
print(v1 <= v2)  # True --- 自动生成
print(v1 >= v3)  # True --- 自动生成
print(v1 != v2)  # True --- 自动生成


# === 5. @functools.wraps ===
# 已在 5.4 节详细讲解,这里展示高级用法

def debug(func):
    """调试装饰器,打印参数和返回值"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        args_repr = [repr(a) for a in args]
        kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
        signature = ", ".join(args_repr + kwargs_repr)
        print(f"Calling {func.__name__}({signature})")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned {result!r}")
        return result
    return wrapper

@debug
def add(a, b):
    return a + b

print(add(3, 4))
# Calling add(3, 4)
# add returned 7
# 7


# === 6. functools.reduce ===
# 已在 5.2 节讲解,这里补充高级用法

# 用 reduce 实现 pipeline
def pipeline(data, *funcs):
    return functools.reduce(lambda acc, f: f(acc), funcs, data)

result = pipeline(
    "  Hello World  ",
    str.strip,
    str.lower,
    lambda s: s.replace(" ", "-"),
)
print(result)  # 'hello-world'

核心差异

特性 Java Python
偏函数 手动实现 functools.partial
缓存 Guava/Caffeine(需配置) @lru_cache 一行搞定
泛型函数 方法重载(编译期) @singledispatch(运行期)
自动生成比较 Comparable 接口 @total_ordering

常见陷阱

python 复制代码
# 陷阱1:lru_cache 的参数必须是可哈希的
@lru_cache(maxsize=128)
def process(data):
    return data

# process([1, 2, 3])  # TypeError: unhashable type: 'list'
process((1, 2, 3))    # OK --- 元组可哈希

# 陷阱2:lru_cache 会"隐藏"参数变化
@lru_cache(maxsize=None)
def query_db(sql):
    # 如果数据库数据变了,缓存不会自动失效
    return f"Result of {sql}"

# 需要手动 cache_clear() 或设置合理的 maxsize

# 陷阱3:singledispatch 只检查第一个参数的类型
@singledispatch
def foo(a, b):
    return "default"

@foo.register(str)
def _(a, b):
    return f"str: {a}, {b}"

print(foo("hello", 42))  # str: hello, 42 --- 根据 a 的类型分派
print(foo(42, "hello"))  # default --- 第一个参数不是 str

何时使用

  • partial:需要复用函数但固定部分参数时
  • lru_cache:纯函数、递归、重复计算------几乎总是好的选择
  • singledispatch:需要根据类型做不同处理时(替代 if-elif-isinstance 链)
  • total_ordering:自定义类需要完整比较操作时

5.6 itertools: 无限迭代器与组合工具

Java/Kotlin 对比

java 复制代码
// Java Stream: 所有操作都是有限的
// 没有 count(), cycle(), repeat() 等无限流
// 组合操作需要手动实现或用 Guava

// Java 生成排列组合
// 需要手写递归或用第三方库
kotlin 复制代码
// Kotlin Sequence: 也是有限的
// 没有内置的无限序列生成器(可以用 sequence { } 模拟)
// 组合操作需要手动实现

Python 实现

python 复制代码
import itertools
from itertools import (
    count, cycle, repeat,
    chain, islice, takewhile, dropwhile,
    product, permutations, combinations, combinations_with_replacement,
    groupby,
)


# === 1. 无限迭代器 ===

# count(start=0, step=1): 无限计数
for i in islice(count(1, 2), 5):  # 必须用 islice 限制!
    print(i, end=" ")  # 1 3 5 7 9
print()

# cycle: 无限循环
colors = ["red", "green", "blue"]
for color in islice(cycle(colors), 7):
    print(color, end=" ")  # red green blue red green blue red
print()

# repeat: 无限重复
for val in islice(repeat("hello"), 3):
    print(val, end=" ")  # hello hello hello
print()

# repeat 带次数限制
print(list(repeat(0, 5)))  # [0, 0, 0, 0, 0]


# === 2. 链接与切片 ===

# chain: 连接多个可迭代对象
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
print(list(chain(list1, list2, list3)))  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# chain.from_iterable: 展平嵌套列表
nested = [[1, 2], [3, 4], [5, 6]]
print(list(chain.from_iterable(nested)))  # [1, 2, 3, 4, 5, 6]

# islice: 切片(适用于任何迭代器,包括无限的)
print(list(islice(count(), 5, 10)))  # [5, 6, 7, 8, 9]
print(list(islice("abcdefg", 2, 6, 2)))  # ['c', 'e']

# takewhile: 条件为真时取值
print(list(takewhile(lambda x: x < 5, [1, 3, 5, 2, 4, 6])))
# [1, 3] --- 遇到 5 就停了,不会继续检查后面的

# dropwhile: 条件为真时跳过
print(list(dropwhile(lambda x: x < 5, [1, 3, 5, 2, 4, 6])))
# [5, 2, 4, 6] --- 跳过前面的 1,3,遇到 5 开始取


# === 3. 组合工具 ===

# product: 笛卡尔积(嵌套循环的替代)
colors = ["red", "blue"]
sizes = ["S", "M", "L"]
print(list(product(colors, sizes)))
# [('red', 'S'), ('red', 'M'), ('red', 'L'), ('blue', 'S'), ('blue', 'M'), ('blue', 'L')]

# 等价于
# for c in colors:
#     for s in sizes:
#         (c, s)

# 带重复的笛卡尔积
print(list(product([1, 2], repeat=3)))
# [(1,1,1), (1,1,2), (1,2,1), (1,2,2), (2,1,1), (2,1,2), (2,2,1), (2,2,2)]

# permutations: 排列(顺序有关)
print(list(permutations([1, 2, 3], 2)))
# [(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)]

# combinations: 组合(顺序无关)
print(list(combinations([1, 2, 3, 4], 2)))
# [(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)]

# combinations_with_replacement: 可重复组合
print(list(combinations_with_replacement([1, 2, 3], 2)))
# [(1,1), (1,2), (1,3), (2,2), (2,3), (3,3)]


# === 4. groupby: 分组(注意:输入必须已排序) ===
data = [
    ("apple", "fruit"),
    ("banana", "fruit"),
    ("carrot", "vegetable"),
    ("date", "fruit"),
    ("eggplant", "vegetable"),
]

# 按类别分组
for key, group in groupby(data, key=lambda x: x[1]):
    print(f"{key}: {[item[0] for item in group]}")
# fruit: ['apple', 'banana']
# vegetable: ['carrot']
# fruit: ['date']  --- 注意!连续相同才分组
# vegetable: ['eggplant']

# 正确用法:先排序
data_sorted = sorted(data, key=lambda x: x[1])
for key, group in groupby(data_sorted, key=lambda x: x[1]):
    print(f"{key}: {[item[0] for item in group]}")
# fruit: ['apple', 'banana', 'date']
# vegetable: ['carrot', 'eggplant']


# === 5. 实战:生成所有可能的密码组合 ===
import string

digits = string.digits  # '0123456789'

# 2位数字密码
passwords_2 = map(
    lambda combo: "".join(combo),
    product(digits, repeat=2)
)
print(list(islice(passwords_2, 5)))  # ['00', '01', '02', '03', '04']


# === 6. 实战:分批处理大数据 ===
def batched(iterable, n):
    """将数据分成每批 n 个(Python 3.12+ 有内置 batched,这里手动实现)"""
    it = iter(iterable)
    while batch := list(islice(it, n)):
        yield batch

data = range(10)
for batch in batched(data, 3):
    print(batch)
# [0, 1, 2]
# [3, 4, 5]
# [6, 7, 8]
# [9]

核心差异

特性 Java Stream Python itertools
无限流 不支持 count, cycle, repeat
笛卡尔积 flatMap 嵌套 product
排列组合 需第三方库 permutations, combinations
分组 Collectors.groupingBy groupby(需预排序)
惰性

常见陷阱

python 复制代码
# 陷阱1:groupby 不会自动排序!
data = [1, 2, 1, 2, 1]
# 错误:直接 groupby
for key, group in groupby(data):
    print(key, list(group))
# 1 [1]
# 2 [2]
# 1 [1]  --- 分成了三组!
# 2 [2]

# 正确:先排序
for key, group in groupby(sorted(data)):
    print(key, list(group))
# 1 [1, 1, 1]
# 2 [2, 2]

# 陷阱2:无限迭代器必须配合 islice 使用
# for i in count():  # 死循环!
#     print(i)

# 陷阱3:groupby 返回的 group 是一次性迭代器
groups = {}
for key, group in groupby(sorted(data)):
    groups[key] = list(group)  # 必须立即消费!

何时使用

  • count/cycle/repeat:需要无限序列时(配合 islice
  • product/permutations/combinations:排列组合、暴力搜索
  • chain:合并多个可迭代对象
  • groupby:有序数据的分组聚合

5.7 生成器: yield 与惰性求值

Java/Kotlin 对比

java 复制代码
// Java: 没有生成器等价物
// 最接近的是 Stream.generate(),但功能有限
// 自定义惰性序列需要实现 Iterator 接口,样板代码多

List<Integer> naturals = Stream.generate(() -> 1)
    .limit(5)
    .collect(Collectors.toList());
kotlin 复制代码
// Kotlin: Sequence 是最接近的概念
val naturals = sequence {
    var n = 0
    while (true) {
        yield(n++)
    }
}
naturals.take(5).toList()  // [0, 1, 2, 3, 4]

Python 实现

python 复制代码
# === 1. yield 基础 ===
def countdown(n):
    """倒计时生成器"""
    while n > 0:
        yield n
        n -= 1

# 生成器是惰性的------不会立即执行
gen = countdown(5)
print(type(gen))  # <class 'generator'>

# 每次调用 next() 执行到下一个 yield
print(next(gen))  # 1
print(next(gen))  # 4
print(next(gen))  # 3

# for 循环消费剩余
for value in gen:
    print(value, end=" ")  # 2 1
print()


# === 2. 生成器 vs 列表:内存优势 ===
def fibonacci_generator(limit):
    """惰性生成斐波那契数列"""
    a, b = 0, 1
    while a < limit:
        yield a
        a, b = b, a + b

# 内存占用:生成器 O(1),列表 O(n)
# 生成 100 万个斐波那契数
import sys

gen = fibonacci_generator(10**100)
print(sys.getsizeof(gen))  # ~200 bytes --- 固定大小!

# 对比列表
small_list = list(fibonacci_generator(10000))
print(sys.getsizeof(small_list))  # ~85KB --- 随数据增长


# === 3. yield from: 委托给子生成器 ===
def upper_letters():
    for c in "ABC":
        yield c

def lower_letters():
    for c in "def":
        yield c

def all_letters():
    yield from upper_letters()  # 委托
    yield from lower_letters()  # 委托
    yield from "123"            # 任何可迭代对象

print(list(all_letters()))  # ['A', 'B', 'C', 'd', 'e', 'f', '1', '2', '3']

# yield from 的实际价值:递归数据结构
def flatten(nested):
    """展平嵌套列表"""
    for item in nested:
        if isinstance(item, (list, tuple)):
            yield from flatten(item)  # 递归委托
        else:
            yield item

data = [1, [2, 3], [4, [5, 6]], 7]
print(list(flatten(data)))  # [1, 2, 3, 4, 5, 6, 7]


# === 4. 生成器表达式 vs 列表推导式 ===
nums = range(10)

# 列表推导式:立即求值,返回列表
squares_list = [x ** 2 for x in nums]
print(type(squares_list))  # <class 'list'>

# 生成器表达式:惰性求值,返回生成器
squares_gen = (x ** 2 for x in nums)
print(type(squares_gen))   # <class 'generator'>

# 语法区别:生成器表达式用圆括号
# 列表推导式用方括号

# 生成器表达式的内存优势
import sys
list_comp = [x ** 2 for x in range(1000000)]
gen_expr = (x ** 2 for x in range(1000000))
print(sys.getsizeof(list_comp))  # ~8.5MB
print(sys.getsizeof(gen_expr))   # ~200 bytes

# 生成器表达式作为函数参数可以省略括号
print(sum(x ** 2 for x in range(5)))  # 30
# 等价于 sum((x ** 2 for x in range(5)))
# 但不需要额外的括号


# === 5. 无限生成器 ===
def natural_numbers():
    """所有自然数(无限)"""
    n = 0
    while True:
        yield n
        n += 1

def primes():
    """素数生成器(无限)"""
    yield 2
    composites = {}
    n = 3
    while True:
        if n not in composites:
            yield n
            composites[n * n] = [n]
        else:
            for p in composites[n]:
                composites.setdefault(p + n, []).append(p)
            del composites[n]
        n += 2

# 取前 10 个素数
from itertools import islice
print(list(islice(primes(), 10)))
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]


# === 6. 生成器的高级用法:管道 ===
def read_lines(filename):
    """模拟逐行读取文件"""
    lines = ["line1\n", "line2\n", "line3\n", ""]
    for line in lines:
        yield line

def strip_lines(lines):
    for line in lines:
        yield line.strip()

def filter_empty(lines):
    for line in lines:
        if line:
            yield line

def upper_lines(lines):
    for line in lines:
        yield line.upper()

# 管道:惰性处理,内存 O(1)
pipeline = upper_lines(filter_empty(strip_lines(read_lines("dummy"))))
print(list(pipeline))  # ['LINE1', 'LINE2', 'LINE3']


# === 7. 生成器方法 ===
def gen_demo():
    yield 1
    yield 2
    yield 3

g = gen_demo()
print(next(g))   # 1
print(g.send(None))  # 2 --- send(None) 等价于 next(g)
g.close()       # 关闭生成器
# next(g)        # StopIteration

# throw: 向生成器抛出异常
def gen_with_throw():
    try:
        yield 1
        yield 2
        yield 3
    except ValueError:
        yield "caught!"

g = gen_with_throw()
print(next(g))           # 1
print(g.throw(ValueError))  # caught!
print(next(g))           # StopIteration

核心差异

特性 Java Kotlin Sequence Python Generator
创建方式 实现 Iterator sequence { } yield
惰性求值
委托 需手动 yieldAll() yield from
生成器表达式 (x for x in ...)
双向通信 send() / throw()

常见陷阱

python 复制代码
# 陷阱1:生成器只能消费一次
gen = (x for x in range(3))
print(list(gen))  # [0, 1, 2]
print(list(gen))  # [] --- 已耗尽

# 陷阱2:在生成器函数中 return 的值
def gen_with_return():
    yield 1
    yield 2
    return "done"  # 触发 StopIteration,值成为异常的 value

g = gen_with_return()
print(next(g))  # 1
print(next(g))  # 2
try:
    next(g)
except StopIteration as e:
    print(e.value)  # 'done'

# 陷阱3:不要对生成器调用 len()
gen = (x for x in range(5))
# len(gen)  # TypeError: object of type 'generator' has no len()

何时使用

  • 处理大数据集时(文件、数据库结果、网络流)
  • 需要惰性求值避免不必要的计算时
  • 管道式数据处理时
  • 无限序列时(必须用生成器)
  • 原则:如果数据量大或可能无限,用生成器;如果数据小且需要多次访问,用列表

5.8 async/await 协程基础

Java/Kotlin 对比

java 复制代码
// Java 21+: Virtual Threads(虚拟线程)
// 本质还是线程模型,不是协程
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    Future<String> future = executor.submit(() -> {
        Thread.sleep(1000);
        return "result";
    });
    String result = future.get();
}

// Java 的异步是 CompletableFuture
CompletableFuture.supplyAsync(() -> fetchData())
    .thenApply(data -> process(data))
    .thenAccept(result -> System.out.println(result));
kotlin 复制代码
// Kotlin: 协程是一等语言特性
suspend fun fetchData(): String {
    delay(1000)  // 挂起函数,不阻塞线程
    return "data"
}

// launch: 启动协程
// async: 启动协程并返回 Deferred
// Flow: 冷流,类似 Python 的异步生成器

Python 实现

python 复制代码
import asyncio


# === 1. async def 定义协程 ===
async def hello():
    """协程函数"""
    print("Hello")
    await asyncio.sleep(0.1)  # 挂起,不阻塞事件循环
    print("World")

# 注意:调用协程函数不会执行!
coro = hello()
print(type(coro))  # <class 'coroutine'>

# 必须用 asyncio.run() 或 await 执行
# asyncio.run(hello())  # Hello → World


# === 2. await 等待协程 ===
async def fetch_data(url: str) -> str:
    """模拟异步 HTTP 请求"""
    print(f"Fetching {url}...")
    await asyncio.sleep(1)  # 模拟网络延迟
    return f"Data from {url}"

async def process():
    """串行执行"""
    data1 = await fetch_data("/api/users")
    data2 = await fetch_data("/api/posts")
    print(data1)
    print(data2)

# asyncio.run(process())
# 总耗时 ~2s(串行)


# === 3. 并发执行: asyncio.gather ===
async def process_concurrent():
    """并发执行"""
    results = await asyncio.gather(
        fetch_data("/api/users"),
        fetch_data("/api/posts"),
        fetch_data("/api/comments"),
    )
    for r in results:
        print(r)

# asyncio.run(process_concurrent())
# 总耗时 ~1s(并发)


# === 4. asyncio.create_task: 后台任务 ===
async def background_job(name: str, seconds: float):
    print(f"{name} started")
    await asyncio.sleep(seconds)
    print(f"{name} finished after {seconds}s")
    return f"{name} result"

async def main():
    # 创建后台任务
    task1 = asyncio.create_task(background_job("Job-A", 0.5))
    task2 = asyncio.create_task(background_job("Job-B", 0.3))

    # 做其他事情
    print("Doing other work...")
    await asyncio.sleep(0.1)
    print("Other work done")

    # 等待任务完成
    result1 = await task1
    result2 = await task2
    print(result1, result2)

# asyncio.run(main())


# === 5. async for: 异步迭代 ===
async def async_range(n: int):
    """异步生成器"""
    for i in range(n):
        await asyncio.sleep(0.1)
        yield i

async def consume_async():
    async for value in async_range(3):
        print(value, end=" ")  # 0 1 2
    print()

# asyncio.run(consume_async())


# === 6. async with: 异步上下文管理器 ===
class AsyncResource:
    async def __aenter__(self):
        print("Acquiring resource...")
        await asyncio.sleep(0.1)
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        print("Releasing resource...")
        await asyncio.sleep(0.1)
        return False

async def use_resource():
    async with AsyncResource() as resource:
        print("Using resource")
        await asyncio.sleep(0.1)

# asyncio.run(use_resource())


# === 7. 超时控制 ===
async def with_timeout():
    try:
        result = await asyncio.wait_for(
            fetch_data("/api/slow"),
            timeout=0.5  # 0.5秒超时
        )
        print(result)
    except asyncio.TimeoutError:
        print("Request timed out!")

# asyncio.run(with_timeout())


# === 8. 实战:并发下载 ===
async def download(url: str) -> tuple[str, str]:
    """模拟下载"""
    await asyncio.sleep(0.1 * len(url))  # 模拟延迟
    return url, f"content of {url}"

async def download_all(urls: list[str]):
    """并发下载所有 URL"""
    tasks = [asyncio.create_task(download(url)) for url in urls]
    results = await asyncio.gather(*tasks)
    return dict(results)

async def main_download():
    urls = ["/a", "/bb", "/ccc", "/dddd"]
    start = asyncio.get_event_loop().time()
    result = await download_all(urls)
    elapsed = asyncio.get_event_loop().time() - start
    print(f"Downloaded {len(result)} URLs in {elapsed:.2f}s")
    print(result)

# asyncio.run(main_download())

核心差异

特性 Java Virtual Threads Kotlin Coroutines Python async/await
模型 线程(轻量级) 协程(挂起) 协程(事件循环)
阻塞操作 需要虚拟线程 suspend 函数 需要 async 版本库
并发原语 ExecutorService launch/async create_task/gather
取消 interrupt cancel cancel
生态 JDK 内置 kotlinx.coroutines asyncio

常见陷阱

python 复制代码
# 陷阱1:在协程中调用阻塞函数会阻塞整个事件循环
import time

async def bad():
    time.sleep(1)  # 阻塞!整个事件循环都停了
    return "done"

# 修复:用 asyncio.sleep 或 run_in_executor
async def good():
    await asyncio.sleep(1)  # 非阻塞挂起
    return "done"

# 阻塞代码用线程池
async def run_blocking():
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(None, lambda: time.sleep(1) or "done")
    return result

# 陷阱2:忘记 await
async def fetch():
    return "data"

async def bad_call():
    result = fetch()  # 没有 await!result 是协程对象,不是数据
    print(result)     # <coroutine object fetch at 0x...>

async def good_call():
    result = await fetch()  # 正确
    print(result)           # 'data'

# 陷阱3:asyncio.run() 不能嵌套
# 如果已经在异步上下文中,不要再次调用 asyncio.run()
# 用 await asyncio.gather() 或 create_task() 代替

何时使用

  • I/O 密集型任务:HTTP 请求、数据库查询、文件操作
  • 需要并发但不需要多线程的复杂度时
  • WebSocket、长轮询等实时通信
  • CPU 密集型任务不要用 asyncio,用 multiprocessing

5.9 contextlib: 上下文管理器

Java/Kotlin 对比

java 复制代码
// Java: try-with-resources(需要实现 AutoCloseable)
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} // reader.close() 自动调用

// 自定义资源
public class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("Resource closed");
    }
}
kotlin 复制代码
// Kotlin: use{} 扩展函数
File("file.txt").bufferedReader().use { reader ->
    reader.lineSequence().forEach { println(it) }
}
// 自动调用 close()

Python 实现

python 复制代码
from contextlib import contextmanager, suppress, redirect_stdout, redirect_stderr
import io


# === 1. with 语句基础 ===
# Python 的 with 等价于 Java 的 try-with-resources

# 内置示例
with open("/tmp/demo.txt", "w") as f:
    f.write("hello")

# 等价于
f = open("/tmp/demo.txt", "w")
try:
    f.write("hello")
finally:
    f.close()


# === 2. 自定义上下文管理器(类方式) ===
class DatabaseConnection:
    def __init__(self, url: str):
        self.url = url
        self.connected = False

    def __enter__(self):
        """进入 with 块时调用"""
        print(f"Connecting to {self.url}")
        self.connected = True
        return self  # 返回值绑定到 as 后的变量

    def __exit__(self, exc_type, exc_val, exc_tb):
        """退出 with 块时调用(包括异常)"""
        print(f"Disconnecting from {self.url}")
        self.connected = False
        # 返回 True 会抑制异常,返回 False(或 None)异常会继续传播
        if exc_type is not None:
            print(f"Exception occurred: {exc_val}")
        return False  # 不抑制异常

    def query(self, sql: str):
        if not self.connected:
            raise RuntimeError("Not connected")
        return f"Result of: {sql}"

with DatabaseConnection("postgresql://localhost/mydb") as db:
    print(db.query("SELECT * FROM users"))
    # Connecting to postgresql://localhost/mydb
    # Result of: SELECT * FROM users
    # Disconnecting from postgresql://localhost/mydb


# === 3. @contextmanager: 用函数创建上下文管理器 ===
@contextmanager
def timer(name: str):
    """计时上下文管理器"""
    import time
    start = time.perf_counter()
    try:
        yield  # yield 之前的代码 = __enter__,之后的代码 = __exit__
    finally:
        elapsed = time.perf_counter() - start
        print(f"[{name}] {elapsed:.4f}s")

with timer("data processing"):
    import time
    time.sleep(0.1)
# [data processing] 0.1001s


# === 4. @contextmanager 带返回值 ===
@contextmanager
def temporary_file(content: str):
    """创建临时文件的上下文管理器"""
    import tempfile
    import os

    fd, path = tempfile.mkstemp()
    try:
        os.write(fd, content.encode())
        os.close(fd)
        yield path  # 返回文件路径
    finally:
        os.unlink(path)  # 确保删除

with temporary_file("hello world") as path:
    print(f"File at: {path}")
    with open(path) as f:
        print(f.read())
# File at: /tmp/tmpXXXXXX
# hello world


# === 5. @contextmanager 处理异常 ===
@contextmanager
def assert_no_exception():
    """如果 with 块中抛出异常,重新抛出包装后的异常"""
    try:
        yield
    except Exception as e:
        raise RuntimeError(f"Caught in context: {e}") from e

try:
    with assert_no_exception():
        raise ValueError("original error")
except RuntimeError as e:
    print(f"Caught: {e}")
    print(f"Caused by: {e.__cause__}")
# Caught: Caught in context: original error
# Caused by: original error


# === 6. contextlib.suppress: 抑制异常 ===
# 等价于 Java 的空 catch 块,但更简洁

# Java 风格
try:
    os.remove("/tmp/nonexistent")
except FileNotFoundError:
    pass

# Python 风格
with suppress(FileNotFoundError):
    os.remove("/tmp/nonexistent")

# 抑制多种异常
with suppress(FileNotFoundError, PermissionError):
    os.remove("/tmp/nonexistent")


# === 7. contextlib.redirect_stdout / redirect_stderr ===
# 重定向标准输出(测试时非常有用)

f = io.StringIO()
with redirect_stdout(f):
    print("This goes to the buffer")
    print("Not to the console")

output = f.getvalue()
print(f"Captured: {output!r}")
# Captured: 'This goes to the buffer\nNot to the console\n'


# === 8. 嵌套上下文管理器 ===
@contextmanager
def acquire_lock(name: str):
    print(f"Lock {name} acquired")
    try:
        yield
    finally:
        print(f"Lock {name} released")

# Python 3.10+ 的写法
with (
    acquire_lock("A"),
    acquire_lock("B"),
):
    print("Both locks held")
# Lock A acquired
# Lock B acquired
# Both locks held
# Lock B released
# Lock A released

# ExitStack: 动态管理多个上下文
from contextlib import ExitStack

with ExitStack() as stack:
    stack.enter_context(acquire_lock("X"))
    stack.enter_context(acquire_lock("Y"))
    print("X and Y held")
# Lock X acquired
# Lock Y acquired
# X and Y held
# Lock Y released
# Lock X released


# === 9. 实战:数据库事务上下文 ===
@contextmanager
def transaction(connection):
    """模拟数据库事务"""
    print("BEGIN TRANSACTION")
    try:
        yield connection
        print("COMMIT")
    except Exception:
        print("ROLLBACK")
        raise

class MockConnection:
    def execute(self, sql):
        print(f"  EXEC: {sql}")

conn = MockConnection()
try:
    with transaction(conn):
        conn.execute("INSERT INTO users VALUES (1, 'Alice')")
        conn.execute("INSERT INTO orders VALUES (1, 100)")
        raise ValueError("Oops!")  # 触发回滚
except ValueError:
    pass
# BEGIN TRANSACTION
#   EXEC: INSERT INTO users VALUES (1, 'Alice')
#   EXEC: INSERT INTO orders VALUES (1, 100)
# ROLLBACK

核心差异

特性 Java try-with-resources Kotlin use{} Python with
接口要求 AutoCloseable Closeable __enter__/__exit__
异常处理 catch 块 try/catch __exit__ 返回值
创建方式 类 或 @contextmanager
动态管理 不支持 不支持 ExitStack
抑制异常 空 catch 空 catch suppress()

常见陷阱

python 复制代码
# 陷阱1:@contextmanager 中 yield 只能出现一次
@contextmanager
def bad():
    yield 1
    yield 2  # RuntimeError: generator didn't stop

# 陷阱2:__exit__ 的返回值
class SilentError:
    def __exit__(self, *args):
        return True  # 抑制所有异常!

with SilentError():
    raise ValueError("This is silently ignored")  # 不会抛出!
print("No error raised")  # 会执行

# 陷阱3:with 块中的变量不会泄漏(Python 3 的改进)
with open("/tmp/demo.txt", "w") as f:
    content = f.write("test")
# f 在这里已经关闭,但变量 f 仍然存在(只是文件已关闭)
# 不要在 with 块外使用 f

何时使用

  • 资源管理:文件、数据库连接、网络连接、锁
  • 临时状态变更:环境变量、工作目录、日志级别
  • 测试:redirect_stdout、临时文件、mock
  • 事务管理:数据库事务、原子操作
  • 原则:任何需要"获取-使用-释放"模式的场景都用上下文管理器

本章总结

概念 JVM 等价物 Python 优势 关键陷阱
一等函数 Kotlin 函数类型 函数可携带属性 lambda 延迟绑定
高阶函数 Stream API zip/enumerate 原生支持 迭代器只能消费一次
闭包 Kotlin lambda nonlocal 显式声明 循环变量绑定
装饰器 无直接等价物 元编程利器 三层嵌套结构
functools Guava/Caffeine @lru_cache 一行缓存 缓存参数须可哈希
itertools 有限 无限迭代器+组合工具 groupby 需预排序
生成器 Kotlin Sequence yield/yield from 只能消费一次
async/await Virtual Threads/Kotlin协程 内置事件循环 阻塞调用会卡死
上下文管理器 try-with-resources @contextmanager __exit__ 返回值

核心原则:Python 的函数式编程不是要你写纯函数式代码,而是给你更强大的工具来表达意图。装饰器和生成器是 Python 最独特的两个特性,掌握它们是从"会写 Python"到"写好 Python"的关键分水岭。

相关推荐
笨鸟先飞,勤能补拙3 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能4 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂4 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study5 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境5 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境6 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1236 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102166 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb007 小时前
一个项目带你入门AI应用开发01
python
0566467 小时前
Python康复训练——常用标准库
开发语言·python·学习