10 个 惊艳的 Pythonic 单行代码
昨天晚上十一点多,我在公司楼下拿着奶茶刷手机,群里有人突然问一句: “有没有那种又短又骚…啊不,又优雅的 Python 单行代码?能装个逼那种。”
我想了下,其实真有不少,而且真的不是花里胡哨,很多我自己线上业务里是天天用的那种。以前写数据库调优、MQ 踩坑的时候我就说过,能用一行优雅写清楚的东西,绝对不多打第二次字
就按聊天的节奏,一个个跟你说,都是可以直接粘贴进项目的。
先说第一个,经典到不能再经典
我感觉只要写过 Python 的都见过,但很多人刚开始不敢用。
第 1 个:一行交换两个变量
a, b = b, a
就是这么朴素,不用临时变量,不用中间过渡。
比如:
a, b = 3, 5
a, b = b, a
print(a, b) # 5 3
你在别的语言里写习惯了 temp = a 那一套,每次看到这个都会有点舒爽,对吧。
第二个,有点“会了就离不开”的感觉
很多人学 Python,真正“入门 Pythonic”,我感觉都是从这个开始的。
第 2 个:一行搞定筛选 + 变换
比如取一串数字里所有偶数的平方:
squares = [x * x for x in nums if x % 2 == 0]
你就想象下传统写法,要先 new 一个列表,再 for,再 if,再 append,一大坨。现在一行就讲清楚了三件事:
我要遍历谁: for x in nums要不要过滤: if x % 2 == 0要把它变成啥: x * x
而且读起来是顺的,不绕。
第三个,处理二维列表的时候特别香
这个我在处理报表数据的时候用得多,比如数据库查出来一堆二维数组,你只想拿里面所有值拼一起。
第 3 个:一行拍扁二维列表
flat = [item for row in matrix for item in row]
看着有点绕,其实脑子里就当成两层 for:
flat = []
for row in matrix:
for item in row:
flat.append(item)
它只是把这俩 for 串成一行了,而且顺序是从左到右越来越细。
如果你哪天写推荐系统、写点简单矩阵处理,这个真的很常用。
第四个,是我特别爱用的字典推导
我经常有这种场景:数据库查出来一堆用户,想按名字快速查分数,就会这样写。
第 4 个:一行把列表变成映射
name_to_score = {u["name"]: u["score"] for u in users}
等价于:
name_to_score = {}
for u in users:
name_to_score[u["name"]] = u["score"]
一行的好处是: 你一眼就知道 key 是啥,value 是啥,来自哪个列表,逻辑特别集中。
再换个用法,经常还能反转一个字典:
reversed_map = {v: k for k, v in original.items()}
当然,前提是 value 没有重复。
第五个,处理字符串列表的时候是必备
比如你有一串 id 想拼成 "1,2,3" 这种样子,很多人上来就 for 循环去拼,其实完全没必要。
第 5 个:一行拼接字符串
csv_line = ",".join(str(x) for x in ids)
这行代码做了三件事:
str(x) for x in ids把每个元素都变成字符串(不管原来是 int 还是别的)","决定了用逗号作为分隔符join把所有字符串连起来
我在做批量接口的时候,经常把一堆 id 拼成 SQL 的 in 条件或者日志字段,全靠它。
第六个,日志排查的时候很好用
这个真的是我深夜排故障时的心头好。比如一堆日志里我要找第一条 ERROR。
第 6 个:一行找到第一个满足条件的元素
first_error = next((line for line in lines if"ERROR"in line), None)
解释一下:
(line for line in lines if "ERROR" in line)是一个生成器,不会一次把所有结果拉出来,省内存next(..., None)会从里面拿第一个,如果没有就给个默认值None
这玩意儿比你写 for + break 那种结构要干净很多,逻辑非常集中: “从这些行里找第一个带 ERROR 的,没有就算了”。
第七个,是统计类需求的“核武器”
你要是还在手写那种 “如果 key 不在字典里就置 0 再 +1” 的写法,真的可以考虑升级一下。
第 7 个:一行搞定 TopN 统计
from collections import Countertop3 = Counter(words).most_common(3)
Counter(words) 会帮你数好每个单词出现几次,most_common(3) 直接给你出现次数最多的 3 个。
比如:
from collections import Counterwords = ["a", "b", "a", "c", "b", "a"]
top3 = Counter(words).most_common(3)
print(top3) # [('a', 3), ('b', 2), ('c', 1)]
写日志分析、评论词频、简单埋点分析,这一招特别省时间。
第八个,是那种“看起来普通,但非常实用”的
很多 Web 项目里,你会有一堆必填字段,做校验的时候不要一行一行 if,其实可以这样写。
第 8 个:一行做表单必填校验
is_valid = all(field.strip() for field in (name, email, phone))
含义就是: 把 name、email、phone 这几个字段都去掉前后空格,只要有一个是空字符串,all 就会返回 False。
改成更通用点的写法也行:
required = (name, email, phone)
is_valid = all(required)
只不过第一种去掉空格,防止用户输入一堆空格骗过校验。
第九个,有点进阶,但真的是业务里常见
比如你拿到一堆订单记录,想按照城市分组,很多人会写个大循环,在里面判断 key 在不在字典里,不如直接用工具人 itertools.groupby。
第 9 个:一行按某个字段分组
from itertools import groupbygrouped = {
city: list(records)
for city, records in groupby(
sorted(orders, key=lambda r: r["city"]),
key=lambda r: r["city"],
)
}
注意几点:
groupby要求事先按同样的 key 排好序,不然分组会乱groupby(..., key=...)会帮你按city聚在一起外面再套一层字典推导,最终得到 {"北京": [...], "上海": [...], ...}这种结构
实际项目里比如按用户、按日期、按产品分组,都可以直接套这个套路,只要改下 key 就行。
第十个,是我个人最喜欢的一个
因为它能很优雅地把 if-elif-elif 那一大坨给“压扁”。
第 10 个:一行搞定“策略分发”
比如有一堆操作类型,不想写成这样:
if action == "create":
handle_create(data)
elif action == "delete":
handle_delete(data)
else:
handle_unknown(data)
那就换成这个:
handler = {
"create": handle_create,
"delete": handle_delete,
}.get(action, handle_unknown)handler(data)
或者如果你一定要严格意义的“单行”,也可以这样写:
{"create": handle_create, "delete": handle_delete}.get(action, handle_unknown)(data)
含义也很直白: 用 action 当 key 去字典里找对应的处理函数,找不到就用默认的 handle_unknown,然后立刻调用。
做接口路由、小型命令解析器、各种“操作类型”处理,这种写法看着很清爽。
差不多就这 10 个,都是我平时真正在写、在线上环境跑过的 Pythonic 单行代码,不是那种为了炫技写出来给人看的。
你完全可以找个现有的小脚本,把里面啰嗦的 for 循环、if 嵌套,慢慢替换成上面这种写法,你会发现代码字数在变少,可读性反而在变好。
行了我先去泡个咖啡,你要是手上有什么自己常用的单行写法,也可以丢几个出来,我们可以一起改一改,看看能 Pythonic 到什么程度。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB