昨天晚上组里同事问我,写Python脚本有没有能“一行干完一堆事儿”的技巧,要既简洁又实用。正好借此机会,整理出10个我工作中频繁使用、颇具Pythonic风格的单行代码示例。它们并非炫技,而是能切实提升代码效率和可读性的实用技巧。
1. 一行过滤与转换:列表推导式
处理日志或数据时,经常需要过滤空值并做转换。传统的循环写法略显冗长:
result = []
for line in lines:
line = line.strip()
if line:
result.append(int(line) * 2)
使用列表推导式可以将其浓缩为清晰的一行:
result = [int(line) * 2 for line in lines if line.strip()]
这行代码依次完成了三件事:遍历列表、过滤掉空白行、将有效内容转换为整数并乘以2。它是替代for循环中append操作的利器。
2. 一行翻转字典:键值互换
有时我们需要根据字典的值反查其键。例如,有一个状态码映射字典:
status_map = {
0: "OK",
1: "ERROR",
2: "RETRY",
}
若想通过"ERROR"找到对应的状态码1,用一行字典推导式即可实现翻转:
reverse_status = {v: k for k, v in status_map.items()}
得到的结果是 {"OK": 0, "ERROR": 1, "RETRY": 2}。需要注意,此方法假设原字典的值是唯一的,否则后出现的键会覆盖前者。
3. 一行统计词频:使用Counter
统计一段文本中单词的出现次数,无需手动循环计数。collections.Counter让这一切变得异常简单:
from collections import Counter
text = "hello world hello python hello"
word_count = Counter(text.split())
word_count的结果将是 {'hello': 3, 'world': 1, 'python': 1}。核心逻辑仅 Counter(text.split()) 一行。如果追求极致的“单行”,甚至可以写成 from collections import Counter; word_count = Counter(text.split()),这在写一次性脚本时非常快捷。
4. 一行实现多级排序
对包含字典的列表进行多字段排序是一个常见需求。例如,有一个用户列表:
users = [
{"name": "张三", "age": 18, "score": 95},
{"name": "李四", "age": 18, "score": 80},
{"name": "王五", "age": 20, "score": 60},
]
若想先按年龄升序,年龄相同时按分数降序排列,一行代码就能解决:
sorted_users = sorted(users, key=lambda u: (u["age"], -u["score"]))
这里sorted函数的key参数返回一个元组(年龄, 分数负值)。元组比较时,会先比较第一个元素(年龄,升序),再比较第二个元素(分数的负值,等价于原分数降序)。
5. 一行判断是否存在:any()与生成器表达式
检查一个可迭代对象中是否存在满足条件的元素,例如判断日志列表中是否包含ERROR。传统写法需要循环和break:
has_error = False
for line in lines:
if "ERROR" in line:
has_error = True
break
用any()配合生成器表达式可以更优雅地实现:
has_error = any("ERROR" in line for line in lines)
any()函数在遇到第一个为True的元素时会立即返回True,同样具备短路特性,效率很高。
6. 一行校验全部条件:all()的妙用
与any()对应,all()用于判断是否所有元素都满足条件。这在配置校验等场景很有用:
config = {
"host": "127.0.0.1",
"port": 3306,
"user": "root",
"password": None, # 故意漏了
}
is_valid = all(config.get(k) is not None for k in ("host", "port", "user", "password"))
如果所有指定键的值都不为None,is_valid为True,否则为False。这种写法比多层if判断更清晰。
7. 一行定位问题行号:enumerate与列表推导
在日志分析中,我们不仅要知道是否存在错误,还需要知道错误发生在第几行。enumerate()可以同时获得索引和元素:
error_lines = [i for i, line in enumerate(lines, start=1) if "ERROR" in line]
这行代码完成了:为每一行附加从1开始的行号、过滤出包含"ERROR"的行、收集这些行的行号。结果error_lines可能类似[3, 10, 28],便于快速定位问题。
8. 一行“压扁”嵌套列表:双层循环推导式
将二维列表(矩阵)扁平化为一维列表,通常需要两层循环。使用嵌套的列表推导式可以一行解决:
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
flat = [x for row in matrix for x in row]
注意顺序:外层的for row in matrix在前,内层的for x in row在后。结果flat为[1, 2, 3, 4, 5, 6, 7, 8, 9]。这在处理API返回的嵌套数据结构时非常方便。
9. 一行合并字典:后者的值优先
合并两个字典,且希望第二个字典的键值对覆盖第一个,是配置管理的常见操作。使用字典解包操作符**可以轻松实现:
default_config = {"host": "127.0.0.1", "port": 3306, "debug": False}
user_config = {"port": 3307, "debug": True}
config = {**default_config, **user_config}
这行代码先将default_config解包,再将user_config解包。后面字典的同名键会覆盖前面的,因此结果config为 {'host': '127.0.0.1', 'port': 3307, 'debug': True}。这比用循环手动合并要简洁直观得多。
10. 一行快速统计耗时
在临时脚本或调试时,想快速了解某个操作耗时,又不想引入复杂的性能分析工具,可以简单使用time模块:
import time; start = time.time(); do_something(); print(f"cost: {time.time() - start:.3f}s")
虽然用分号写在一行不够优雅,但在快速测试、用完即删的场景下非常方便,能一眼看出某个步骤是否耗时。
更多实用单行技巧
除此之外,还有许多常用的单行技巧:
- 变量交换:
a, b = b, a
- 读取文件非空行:
lines = [l.strip() for l in open(“data.txt”, encoding=”utf-8") if l.strip()]
- 去重并保持原顺序:
unique = list(dict.fromkeys(items))
掌握这些单行代码的关键在于多实践、多阅读优秀的Python代码。但请记住,追求“Pythonic”的前提是保证代码的可读性。只有那些让人一眼就能看懂的简洁代码,才是真正优雅和高效的。如果你对这类提升编码效率的技巧感兴趣,欢迎在云栈社区的Python板块与更多开发者交流探讨。