Python 惯用法
下面是一份 最实用、最常用、最优雅且高效的 Python 惯用法(Idioms)+ 风格约束清单,覆盖从语法、数据结构、函数式、错误处理、性能到架构等方面。
这是实际工程常见的最佳实践,适合日常写业务代码、基础库、或做大型项目。
squares = [x*x for x in nums]
unique_values = {x for x in data}
index = {item.id: item for item in items}
a, b = b, a
for i, v in enumerate(items):
...
for a, b in zip(list1, list2):
...
if any(v < 0 for v in nums):
print("有负数")
s = ",".join(names)
a, _, c = my_tuple
f"User {user_id} has {count} messages"
Python 会自动将对象转换为布尔值。
None, False, 0, "", [], {}, set() 等被视为假值。
if obj: 比 if obj != None: 或 if len(obj) > 0: 更惯用。
🌟🌟 判断对象的类型, 以及对象的数据是否为真:
if isinstance(object, typeClass) and object:
...
- 常用于判断参数的类型以及参数的数据是否为真
dict.get(key, default)是安全获取字典值的方式,避免KeyError。dict.setdefault(key, default)是在键不存在时设置默认值的便捷方式。
value = config.get("timeout", 30)
# 安全获取,提供默认值
count = my_dict.get('key', 0)
# 设置默认值
my_dict.setdefault('key', []).append('value')
my_dict = {'a': 1, 'b': 2}
# Key 'a' exists, so its value (1) is returned, and the dict is unchanged.
value1 = my_dict.setdefault('a', 100)
print(f"Value 1: {value1}")
print(f"Dict 1: {my_dict}")
# Key 'c' does not exist, so it is added with the value 3, which is returned.
value2 = my_dict.setdefault('c', 3)
print(f"Value 2: {value2}")
print(f"Dict 2: {my_dict}")
if x in fast_lookup_set:
...
from collections import Counter
freq = Counter(words)
from collections import defaultdict
d = defaultdict(list)
d[key].append(value)
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
list(map(str.upper, names))
[x.upper() for x in names]
sorted(users, key=lambda u: u.age)
from functools import reduce
total = reduce(lambda a, b: a + b, nums)
try:
...
except ValueError:
...
with open("data.txt") as f:
text = f.read()
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove("tmp")
❌ 不推荐:
if key in d:
x = d[key]
✅ 推荐:
try:
x = d[key]
except KeyError:
...
else 子句在 try 块没有引发异常时执行。
finally 子句无论是否发生异常都会执行,常用于清理资源。
try:
f = open('file.txt')
except FileNotFoundError:
print("File not found!")
else:
# 只有在没有异常时才执行
content = f.read()
f.close()
finally:
# 无论是否有异常都会执行
print("Cleanup done.")
await asyncio.gather(task1(), task2())
task = asyncio.create_task(worker())
参考 async 中的 run_cpu_async
from pathlib import Path
path = Path("data") / "a.txt"
text = path.read_text()
with open("log.txt", "w") as f:
f.write("ok")
def read_lines(path):
with open(path) as f:
for line in f:
yield line.strip()
def calc():
total = 0
append = lst.append
for i in range(100000):
append(i)
if status in {"success", "ok"}:
...
检查一个元素是否存在于序列(如列表、字符串、元组)或集合(如集合、字典)中,in 是最清晰、最高效的方式。
if 'Py' in 'Python':
print("Found 'Py'")
if 'key' in my_dict:
print("Key exists in dictionary")
from functools import lru_cache
@lru_cache
def heavy(x):
...
遵循上下文管理器协议,确保资源(如文件、网络连接、锁等)在使用后能被正确地清理,即使发生异常也是如此。这是处理资源的标准方式。
# 好的惯用法:自动关闭文件
with open('somefile.txt', 'r') as f:
content = f.read()
# f 在这里自动关闭,无需手动调用 f.close()
# 避免手动管理资源
# f = open('somefile.txt', 'r')
# content = f.read()
# f.close() # 可能忘记,或在 read 时出错导致 close 不被调用
def load(): ...
def validate(): ...
def write(): ...
def get_db():
with SessionLocal() as db:
yield db
- 标准库
- 第三方库
- 本地项目模块
这是 Python 中的一个标准模式,用于确保模块在作为脚本直接运行时才执行特定代码块(如 main 函数或测试代码),而在被导入时则不会执行。
# my_module.py
def my_function():
return "Hello from module!"
if __name__ == '__main__':
# 这部分代码只在直接运行 my_module.py 时执行
print(my_function())
- 变量名: 使用小写字母和下划线,例如:
my_variable - 常量名: 使用全部大写字母和下划线,例如:
MAX_VALUE - 类名: 使用驼峰命名法,每个单词首字母大写,例如:
MyClass - 函数名: 使用小写字母和下划线,例如:
my_function
比如:只使用 1次 的变量
- ❌ a, b, c
- ✅ user_id, file_path, timeout_ms
def add(x: int, y: int) -> int:
return x + y
- ❌ 有时返回对象,有时返回 None
- ✅ 保持统一返回值类型
DEFAULT_TIMEOUT = 30
is 检查两个变量是否指向内存中的同一个对象。对于 None 这样的单例,使用 is 比 == 更精确、更高效。
if value is None:
print("Value is not set")
# 避免
# if value == None:
# print("Value is not set") # 不推荐
利用 or 运算符的短路求值特性,如果左侧操作数为假值,则返回右侧操作数。
# b = z(x=n or m) # 如您之前的问题,这是惯用法
config_value = user_input or 'default_value'
repr() 旨在生成一个明确的、面向开发者(或解释器)的对象表示形式。
str() 旨在生成一个易于阅读、面向用户( 或 print() )的对象表示形式。
- 使用 4 个空格缩进
- 使用
snake_case命名函数、变量、方法和模块 - 使用
UpperCamelCase命名类 - 使用
UPPER_CASE_WITH_UNDERSCORES命名常量 - 在运算符周围使用空格:
a = b + c - 在逗号后使用空格:
func(a, b, c) - 在函数和类之间使用两个空行,方法之间使用一个空行。
Pythonic 的本质不是“写短”,而是“写可读、表达意图清晰、别人一眼能懂的代码”。
- Python 的语法很容易把复杂的意思挤到同一行表达式里,这样写很难懂。
- 复杂的表达式,尤其是那种需要重复使用的复杂表达式,应该写到辅助函数里面。
- 用 if/else 结构写成的条件表达式,要比用 or 与 and 写成的 Boolean 表达式更好懂。
- 笔记:写公用方法,复用。
# ❌
red = int(my_values.get('red', [''])[0] or 0)
# ❌
red_str = my_values.get('red', [''])
red = int(red_str[0]) if red_str[0] else 0
# ✅
def get_first_int(values, key, default=0):
found = values.get(key, [''])
if found[0]:
return int(found[0])
return default
red = get_first_int(my_values, 'red')
green = get_first_int(my_values, 'green')
- unpacking 是一种特殊的 Python 语法,只需要一行代码,就能把数据结构里面的多个值分别赋给相应的变量。
- unpacking 在 Python 中应用广泛,凡是可迭代的对象都能拆分,无论它里面还有多少层迭代结构。
- 尽量通过 unpacking 来拆解序列之中的数据,而不要通过下标访问,这样可以让代码更简洁、更清晰。
favorite_snacks = {
"salty": ("pretzels", 100),
"sweet": ("cookies", 180),
"veggie": ("carrots", 20)
}
((type1, (name1, cals1)),
(type2, (name2, cals2)),
(type3, (name3, cals3))) = favorite_snacks.items()
print(f'Favorite {type1} is {name1} with {cals1} calories')
print(f'Favorite {type1} is {name1} with {cals1} calories')
print(f'Favorite {type1} is {name1} with {cals1} calories')