Skip to main content
Documents
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Python 惯用法

下面是一份 最实用、最常用、最优雅且高效的 Python 惯用法(Idioms)+ 风格约束清单,覆盖从语法、数据结构、函数式、错误处理、性能到架构等方面。

这是实际工程常见的最佳实践,适合日常写业务代码、基础库、或做大型项目。

☘️ 第一部分:表达式与语法(最常用)

使用列表推导式而不是 for + append

squares = [x*x for x in nums]

使用集合/字典推导式

unique_values = {x for x in data}
index = {item.id: item for item in items}

使用多变量解包(swap 无需 temp)

a, b = b, a

使用 enumerate 替代 range(len)

for i, v in enumerate(items):
    ...

使用 zip 同步遍历多个序列

for a, b in zip(list1, list2):
    ...

使用 any() / all() 做逻辑判断

if any(v < 0 for v in nums):
    print("有负数")

使用 join 拼接字符串(不要用 “+”)

s = ",".join(names)

使用 _ 表示无用变量

a, _, c = my_tuple

使用 f-string(比 format/readability 强太多)

f"User {user_id} has {count} messages"

使用 if obj: 进行逻辑值检测

Python 会自动将对象转换为布尔值。

None, False, 0, "", [], {}, set() 等被视为假值。

if obj:if obj != None:if len(obj) > 0: 更惯用。

🌟🌟 判断对象的类型, 以及对象的数据是否为真:

if isinstance(object, typeClass) and object:
    ...
  • 常用于判断参数的类型以及参数的数据是否为真

☘️ 第二部分:数据结构惯用法

用 dict.get(key, default) 和 dict.setdefault(key, default) 简化判断

  • 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}")

用 set 提升查找速度(O(1))

if x in fast_lookup_set:
    ...

用 collections.Counter

from collections import Counter
freq = Counter(words)

用 defaultdict 简化嵌套结构

from collections import defaultdict
d = defaultdict(list)
d[key].append(value)

用 namedtuple / dataclass 表达数据结构

from dataclasses import dataclass

@dataclass
class User:
    id: int
    name: str

☘️ 第三部分:函数式(map / filter / reduce / lambda)

map/filter 用于简短场景

list(map(str.upper, names))

更推荐列表推导式(可读性更强)

[x.upper() for x in names]

使用 lambda 做简单函数

sorted(users, key=lambda u: u.age)

reduce 适合累加类需求

from functools import reduce
total = reduce(lambda a, b: a + b, nums)

☘️ 第四部分:异常处理惯用法

尽量捕捉 specific 的错误,而不是裸 except

try:
    ...
except ValueError:
    ...

使用 context manager(with)简化资源管理

with open("data.txt") as f:
    text = f.read()

使用 contextlib

from contextlib import suppress
with suppress(FileNotFoundError):
    os.remove("tmp")

避免行级 try/except(性能差,可读性差)

❌ 不推荐:

if key in d: 
    x = d[key]

✅ 推荐:

try:
    x = d[key]
except KeyError:
    ...

使用 try/except/else/finally

else 子句在 try 块没有引发异常时执行。

finally 子句无论是否发生异常都会执行,常用于清理资源。

try:
    f = open('file.txt')
except FileNotFoundError:
    print("File not found!")
else:
    # 只有在没有异常时才执行
    content = f.read()
    f.close()
finally:
    # 无论是否有异常都会执行
    print("Cleanup done.")

☘️ 第五部分:异步与并发惯用法

使用 asyncio 的内建方法

await asyncio.gather(task1(), task2())

使用 asyncio.create_task 启动后台任务

task = asyncio.create_task(worker())

避免 async 阻塞 CPU,用 run_in_executor

参考 async 中的 run_cpu_async


☘️ 第六部分:文件与路径

Pathlib 替代 os.path

from pathlib import Path
path = Path("data") / "a.txt"
text = path.read_text()

使用 with 管理打开文件

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)

使用 in 替代多层 or 判断

if status in {"success", "ok"}:
    ...

使用 in 进行成员资格测试

检查一个元素是否存在于序列(如列表、字符串、元组)或集合(如集合、字典)中,in 是最清晰、最高效的方式。

if 'Py' in 'Python':
    print("Found 'Py'")

if 'key' in my_dict:
    print("Key exists in dictionary")

使用 lru_cache 缓存重复函数结果

from functools import lru_cache

@lru_cache
def heavy(x):
    ...

使用 with 语句进行资源管理

遵循上下文管理器协议,确保资源(如文件、网络连接、锁等)在使用后能被正确地清理,即使发生异常也是如此。这是处理资源的标准方式。

# 好的惯用法:自动关闭文件
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(): ...

使用依赖注入(特别是 FastAPI)

def get_db():
    with SessionLocal() as db:
        yield db

配置文件外置(json / yaml / toml)

模块名称全小写,类名用 PascalCase

引入顺序规范

  1. 标准库
  2. 第三方库
  3. 本地项目模块

使用 if __name__ == '__main__': 进行模块保护

这是 Python 中的一个标准模式,用于确保模块在作为脚本直接运行时才执行特定代码块(如 main 函数或测试代码),而在被导入时则不会执行。

# my_module.py
def my_function():
    return "Hello from module!"

if __name__ == '__main__':
    # 这部分代码只在直接运行 my_module.py 时执行
    print(my_function())

☘️ 第九部分:优雅代码的写法(非常重要)

写短函数(一般不超过 40 行)

名称

  • 变量名: 使用小写字母和下划线,例如: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
  • ✅ 保持统一返回值类型

避免神奇数字(magic number)

DEFAULT_TIMEOUT = 30

使用 is 进行单例(如 None, True, False)和身份比较

is 检查两个变量是否指向内存中的同一个对象。对于 None 这样的单例,使用 is== 更精确、更高效。

if value is None:
    print("Value is not set")

# 避免
# if value == None:
#     print("Value is not set") # 不推荐

使用 or 进行默认值赋值

利用 or 运算符的短路求值特性,如果左侧操作数为假值,则返回右侧操作数。

# b = z(x=n or m) # 如您之前的问题,这是惯用法
config_value = user_input or 'default_value'

使用 repr() 和 str()

repr() 旨在生成一个明确的、面向开发者(或解释器)的对象表示形式。

str() 旨在生成一个易于阅读、面向用户( 或 print() )的对象表示形式。

遵循 PEP 8 代码风格

  • 使用 4 个空格缩进
  • 使用 snake_case 命名函数、变量、方法和模块
  • 使用 UpperCamelCase 命名类
  • 使用 UPPER_CASE_WITH_UNDERSCORES 命名常量
  • 在运算符周围使用空格: a = b + c
  • 在逗号后使用空格: func(a, b, c)
  • 在函数和类之间使用两个空行,方法之间使用一个空行。

☘️ 第十部分:最体现 Pythonic 心法的一句话

Pythonic 的本质不是“写短”,而是“写可读、表达意图清晰、别人一眼能懂的代码”。


Effective

用辅助函数取代复杂的表达式

  • 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 (解包)

  • 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')