Python技术迷

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 Counter

top3 = Counter(words).most_common(3)

Counter(words) 会帮你数好每个单词出现几次,most_common(3) 直接给你出现次数最多的 3 个。

比如:

from collections import Counter

words = ["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 groupby

grouped = {
    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