📢 本文是「Java程序员180天转型AI」系列第24课,建议收藏后阅读
✅ Pythonic写法 —— 一行代码顶十行

▲ 列表推导式是 Python 的招牌语法,写起来像念诗
一、开篇:为什么 Python 代码能这么短?🎯
Java 程序员写惯了 for 循环,一个列表过滤加映射,至少七八行。Python 有列表推导式,一行搞定。这不是炫技,而是 Python 社区推崇的写法,叫 Pythonic。
今天我们要掌握:
• 列表推导式:过滤 + 映射,一行完成• 字典/集合推导式:推导式的扩展• 生成器表达式:惰性求值,省内存• yield:自定义生成器函数• 对比 Java 的 Stream API
二、列表推导式:过滤 + 映射,一行搞定 📝
列表推导式的语法是:[表达式 for 变量 in 可迭代对象 if 条件]。左边是映射,右边是过滤。
# 场景:把成绩列表中大于60分的提取出来,并加5分 scores = [55, 78, 82, 45, 91, 67] # 传统写法(Java风格) result = [] for s in scores: if s > 60: result.append(s + 5) # 列表推导式(Pythonic) result = [s + 5 for s in scores if s > 60] # 结果:[83, 87, 96, 72] # 对比Java: // List<Integer> result = scores.stream() // .filter(s -> s > 60) // .map(s -> s + 5) // .collect(Collectors.toList());// Python 的列表推导式比 Java Stream 更简洁,不用写 lambda 箭头
列表推导式的执行顺序是从右往左:先遍历 scores,再过滤 s > 60,最后映射 s + 5。刚开始可能不习惯,写多了就顺手了。
💡 Java程序员的坑: • 列表推导式外面是方括号 [],不是圆括号或花括号 • if 条件在 for 后面,顺序别写反 • 嵌套推导式可读性差,超过两层建议拆成普通循环• 推导式比循环快一点,但差距不大,不要为了性能硬用
三、字典 & 集合推导式:推导式的扩展 📦
推导式不止列表能用,字典和集合也可以。语法类似,只是括号不同。
# 字典推导式:把姓名列表转成 姓名:长度 的字典 names = ["Alice", "Bob", "Charlie"] name_dict = {n: len(n) for n in names} # {'Alice': 5, 'Bob': 3, 'Charlie': 7} # 集合推导式:提取所有成绩中的个位数 scores = [85, 92, 78, 91]<nunits = {s % 10 for s in scores} # {1, 2, 5, 8} # 对比Java: // Map<String, Integer> map = new HashMap<>(); // for (String n : names) { map.put(n, n.length()); }// Python 一行,Java 四行,这就是差距
字典推导式在数据转换时特别有用,比如把 DataFrame 的某一列转成字典做映射。集合推导式自动去重,适合提取唯一值。
四、生成器表达式:省内存的惰性求值 💾
列表推导式一次性生成所有结果,存在内存里。如果数据量很大,比如处理百万条日志,内存会爆。生成器表达式用圆括号代替方括号,惰性求值,用一次算一次。
# 列表推导式:一次性生成100万个元素,占内存 squares_list = [x**2 for x in range(1000000)] # 生成器表达式:不占用内存,用的时候再算 squares_gen = (x**2 for x in range(1000000)) # 使用生成器 for s in squares_gen: if s > 100: break # 只计算了前11个元素,后面的根本没算 # 生成器只能遍历一次,用完就没了 # 如果需要多次使用,转成列表:list(squares_gen) # 对比Java: // Java 的 Stream 也是惰性求值,但语法更啰嗦 // IntStream.range(0, 1000000).map(x -> x * x)// Python 的生成器表达式更轻量,不需要流式API的包装
生成器的核心特点是:惰性、一次性、省内存。处理大数据时,优先考虑生成器而不是列表。
五、yield:自定义生成器函数 🔄
生成器表达式适合简单场景,复杂逻辑需要自定义函数。在函数里用 yield 代替 return,函数就变成了生成器。
# 自定义生成器:读取大文件,每次返回一行 def read_lines(filename): with open(filename, "r") as f: for line in f: yield line.strip() # 使用 for line in read_lines("big_file.txt"): print(line) # 处理完一行,内存里只有这一行 # 对比Java: // Java 实现类似功能需要实现 Iterator 接口 // 或者用 Stream 的 Supplier,代码量至少是3倍// Python 的 yield 让生成器变得像写普通函数一样简单
yield 的妙处在于:函数执行到 yield 时暂停,下次从暂停处继续。这让无限序列、流式处理变得非常容易实现。
六、综合对比:Java Stream vs Python 推导式 📊
七、今日实战:数据处理流水线 🎯
# 模拟:从原始成绩中提取有效数据,计算等级 raw_scores = [55, 78, 82, 45, 91, 67, 88, 93, 59, 76] # 列表推导式:提取及格成绩,换算成百分制等级 grades = [ {"score": s, "level": "A" if s >= 90 else "B" if s >= 80 else "C"} for s in raw_scores if s >= 60 ] # 字典推导式:统计各等级人数 from collections import Counter level_counts = {g["level"]: sum(1 for x in grades if x["level"] == g["level"]) for g in grades} # 生成器:逐个输出等级信息 def grade_generator(grades): for g in grades: yield f"成绩{g['score']}分,等级{g['level']}" for msg in grade_generator(grades): print(msg)
✅ 这个练习涵盖了今天所有知识点: • 列表推导式(过滤 + 映射 + 条件表达式) • 字典推导式 • yield 自定义生成器• 嵌套条件表达式(A/B/C 等级)
八、今日任务 🚀
📌 今日任务
✅ 把一段 for 循环改写成列表推导式 ✅ 用字典推导式把列表转成字典 ✅ 用生成器表达式处理一万个数据,观察内存占用 ✅ 写一个带 yield 的生成器函数 ✅ 对比 Java Stream 和 Python 推导式的写法差异
📌 第25课预告
装饰器 —— 像AOP一样理解它
我们会聊: • 函数装饰器(对比Java的注解 + 代理模式) • @staticmethod / @classmethod • 自定义装饰器 • 装饰器在AI项目中的应用场景
📌 所有软件和后续代码流程都免费分享,有需要关注后发送ai课程即可。本节课程源码和教学视频已经放在老位置了,刷新后即可获取!建议将我设为星标,避免错过每日课程。坚持180节课,预祝我们转型成功!
如果这篇文章对你有帮助,欢迎点赞、收藏、转发给身边的Java同行。明天见!👋
夜雨聆风