2025-08-18
Python基础
00
请注意,本文编写于 124 天前,最后修改于 124 天前,其中某些信息可能已经过时。

目录

前言
Python风格规范
1. 统一使用4个空格缩进
2. 单行最大长度为100
3. 行数超过规定,建议用小括号()将多行内容连接起来,而不推荐使用反斜杠\进行连接。具体例子如下:
5. 空行使用
6. 空格使用
命名
Python 函数设计规范
1. 强制规则
2. 推荐规则
3. 常见反例与正确写法
Python 模块导入规则
1. 导入位置:文件顶部
2. 导入分组和顺序
3. 导入的例外/特殊场景
注释
1. Docstring(接口注释,使用 """)
2. TODO / FIXME / HACK / NOTE
真值判断
1. 容器类型空值判断(推荐使用布尔值而非长度比较)
2. None 值判断
3. 布尔类型判断
4. 综合实例
异常捕捉
1. 异常捕捉原则
2. 推荐做法
3. 异常处理结构示例
项目工程结构
1. 项目根目录文件
2. 核心代码目录 app/
3. 配置目录 config/
4. 部署目录 deploy/
5. 数据库目录 db/
6. 工具目录 utils/
7. 测试目录 tests/

前言

  我们学习一门语言,既要服务于我们所解决的问题, 也要对语言的设计者对设计该语言的初衷进行了解。"经验丰富的程序员倡导尽可能避繁就简。Python社区的理念都包含在Tim Peters撰写的 “Python之禅”中。要获悉这些有关编写优秀Python代码的指导原则,只需在解释器中执行命令 import this"(引用《Python编程:从入门到实践》),以下让我们对"Python之禅"的内容进行简单的翻译,没事儿看看,可能在不同的编程阶段会有不同的感悟。

bash
The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
  1. 优美胜于丑陋(Python 以编写优美的代码为目标)
  2. 明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
  3. 简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
  4. 复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
  5. 扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套,少写一些复杂的列表推倒式,对自己对别人都友好)
  6. 间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
  7. 可读性很重要(优美的代码是可读的)
  8. 即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)
  9. 不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except
    风格的代码)
  10. 当存在多种可能,不要尝试去猜测
  11. 而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
  12. 虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido )
  13. 做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量,但不要过分考虑,开始就追求完美并不是一个好主意,追求完美最好分阶段进行)
  14. 如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)
  15. 命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)

Python风格规范

1. 统一使用4个空格缩进

2. 单行最大长度为100

3. 行数超过规定,建议用小括号()将多行内容连接起来,而不推荐使用反斜杠\进行连接。具体例子如下:

  • 函数/方法参数过长
python
# ✅ 推荐:括号隐式换行 + 每行一个参数 + 可加尾随逗号 result = do_something( user_id=uid, retries=3, timeout=5.0, on_error=handle_error, # 行内注释也没问题 ) # ❌ 不推荐:反斜杠续行,注释/空格稍不注意就报错 result = do_something( \ user_id=uid, \ retries=3, \ timeout=5.0, \ on_error=handle_error \ )
  • 构造列表/字典/集合
python
# ✅ [] / {} 内天然支持多行 fields = [ "id", #用户ID "name", "email", # 轻松加注释 ] config = { "host": "localhost", # 测试服IP "port": 5432, "debug": True, }
  • 复杂布尔表达式(建议“操作符前置”)
python
# ✅ 推荐:把每个子条件单独一行,且操作符前置,便于增删 if ( is_logged_in and has_permission("export") and (items_count > 0) ): export()
  • with / import 多项
python
# ✅ 多个上下文管理器 with ( open("input.txt") as fin, open("output.txt", "w") as fout, ): fout.write(fin.read()) # ✅ 多个导入(小括号内自动换行) from pathlib import ( Path, PurePath, )
  • 长字符串拼接(相邻字面量自动拼接 + 括号包裹)
python
# ✅ 相邻字符串字面量会在编译期合并 sql = ( "SELECT id, name, email " "FROM users " "WHERE active = 1 " "ORDER BY created_at DESC" ) # ✅ f-string 也可以拆成多个相邻 f-string user_line = ( f"id={user.id} " f"name={user.name} " f"email={user.email}" )
  • 数学/链式调用过长
python
# ✅ 保持结构清晰 score = ( 0.25 * precision + 0.25 * recall + 0.50 * f1 ) # ✅ 链式调用 ( pd.read_csv("data.csv") .query("age >= 18") .assign(is_vip=lambda df: df["score"] > 80) .to_csv("out.csv", index=False) )
  • 长的类型注解(typing)
python
from typing import Dict, List, Tuple # ✅ 类型别名分行清晰 UserIndex = Dict[ int, # user_id Tuple[str, List[str]] # (name, tags) ]
  • 推导式也可用括号(注意:小括号会变成生成器表达式)
python
# ✅ 方括号推导式可多行(仍是列表) names = [ user.name for user in users if user.active ] # ✅ 小括号=生成器表达式,传给 sum 等函数更省内存 total = sum( item.price for item in items if item.in_stock )
  • 正确地给长字典/列表“加注释 + 尾随逗号”
python
# ✅ 尾随逗号让 diff 更干净,后续新增行无需改上一行 settings = { "timeout": 10, # seconds "retries": 3, "cache": True, }
  • 较长的 return/dict() 构造
python
def build_payload(user): return { "id": user.id, "name": user.name, "roles": [r.name for r in user.roles], "active": user.active, }
  • 常用范式模版
python
todo: 范式 A:长调用参数 resp = requests.post( url, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }, json={ "id": user.id, "name": user.name, "tags": user.tags, }, timeout=10, ) todo: 范式 B:长条件判断 if ( user.is_active and user.email_verified and not user.is_banned and (quota.used < quota.limit) ): allow_access() todo: 范式 C:长字符串(SQL/日志模板) query = ( "SELECT u.id, u.name, COUNT(o.id) AS orders " "FROM users u " "LEFT JOIN orders o ON u.id = o.user_id " "WHERE u.created_at >= :start " "GROUP BY u.id, u.name " "ORDER BY orders DESC " "LIMIT 100" ) todo: 范式 D:分步计算/链式 final = ( normalize(raw) .pipe(filter_invalid) .pipe(enrich_with_geo) .pipe(attach_metrics) )
  • 反例
python
# ❌ 行尾注释与 \ 冲突 total = price + tax \ # 因为反斜杠后还有东西(注释),所以这个反斜杠失效,并不会把下一行接起来 + fee # Python 会尝试单独解析 + fee 这一行,但这在语法上是不完整的表达式,直接报错: SyntaxError: invalid syntax """ 1. \ 表示“当前行还没结束,请继续看下一行”。 2. 但是 \ 必须是这一行的最后一个字符,后面不能有空格、不能有注释。 否则 Python 认为 \ 只是一个普通字符,行就结束了。 """

5. 空行使用

  1. 函数之间用两个空行隔开
  2. 类之间用两个空行隔开
  3. 类中方法用一个空行隔开
  4. 函数中不同逻辑代码块之间可适当插入空行
  5. 文件末尾必须有一个空行(有助于 diff 友好和工具处理)

6. 空格使用

  1. 在二元运算符两边都要有空格。二元运算包括:算术(+ - * / ** )、赋值(=,+=,-=)、比较( ==, <, >, !=, in, not in, is, is not)、逻辑运算(and or not)、位运算(& | ! ~ >> <<)、海象运算符 :=
  2. 函数关键字参数=两侧不需要空格。例: res = func(name="Tom")
  3. 逗号后面要加空格,但是如果后面是小括号则不用。例:List=[1, 2, 4]
  4. 冒号前不加空格,冒号后要加空格。但是切片里前后都不可加空格 。例:Dict = {key: value}
  5. 不要为对齐赋值语句而使用的额外空格
  6. 切片特殊规则:默认切片arr = a[1:5]不要加空格、如果切片表达式本身比较复杂,可以用 对称空格 提高可读性如arr = a[lower : upper + 1]

命名

类型命名方式✅ 示例❌ 反例说明
普通变量snake_casemax_value, user_nameMaxValue, maxValue统一小写+下划线;布尔变量常用 is_, has_
关键字冲突变量末尾加 _class_, type_class, type避免与关键字/内置遮蔽
常量ALL_CAPSMAX_RETRY, DEFAULT_TIMEOUTMaxRetry, maxretry模块顶层定义;可配合 Final
函数名snake_casedef get_user_info():def GetUserInfo():动作/动宾短语;参数命名约定 self, cls
类名CapWords(驼峰)class UserProfile:class user_profile:异常类常以 Error 结尾
模块名全小写,可下划线user_service.py, config.pyUserService.py文件名简短可读
包名全小写(不建议下划线)services, utilsmy_package导入路径简洁,生态约定
单前导下划线_name_cache, _connect()cache, connect约定为“内部使用”,非强制私有
双前导下划线__name__balance, __compute()balance, compute触发名称改写,避免子类冲突
双前后下划线__name____init__, __str____my_func__保留“魔术方法”,禁止自造
异常类驼峰,Error 结尾class ConfigError(Exception):class Config(Exception):明确错误语义
布尔变量语义前缀is_valid, has_permissionvalid, permission可读性更强
集合/复数复数形式users, items, ordersuser_list, itemArray建议用含义清晰的复数名
测试函数test_ 前缀test_parse_config()parse_config_test()与 pytest/工具链兼容

Python 函数设计规范

1. 强制规则

规则要点✅ 正例❌ 反例 / 解释
函数尽量短小100 行作为上限参考;更小更好将长流程拆为若干私有/辅助函数一个函数数百行、承担多种职责
单一职责一个函数只做一件事;语句粒度保持一致parse_config() 只负责解析,不负责 IO/校验parse_config() 又读文件、又解析、又写 DB
禁用可变类型作默认值默认参数在定义时只评估一次 → 共享状态风险def f(x=None):\n if x is None:\n x = []def f(x=[]): ...(不同调用共享同一列表)
禁止“共享可变默认值”套路千万别把模块级列表/字典拿来当默认值DEFAULT_FACTORY = list + 在函数里 x = DEFAULT_FACTORY()List = []\n def fun(num, arr=List): ...明令禁止

提示

数据类可以用 default_factory

python
from dataclasses import dataclass, field @dataclass class Bag: items: list[int] = field(default_factory=list) # 每次实例化时都给我一个全新的空列表

2. 推荐规则

规则要点✅ 正例备注
self / cls 命名实例方法首参用 self类方法首参用 cls静态方法self/clspython\nclass User:\n def save(self): ...\n @classmethod\n def from_id(cls, uid): ...\n @staticmethod\n def ping(): ...\n你原文里的“类静态方法用 cls”应更正为类方法
关键字冲突的参数名若与关键字冲突,末尾加下划线 _def select(from_: str): ...与变量命名冲突处理保持一致
圈复杂度 ≤ 1010 为上限;超过就拆分提取子函数、使用早返回、用查表/策略替代多层 if-elif可用 radon/ruff 扫描(团队 CI)

3. 常见反例与正确写法

1) 可变默认值

python
# ❌ 错误:默认列表在定义时创建,后续调用共享同一对象 def append_item(item, bucket=[]): bucket.append(item) return bucket append_item(1) # [1] append_item(2) # [1, 2] ← 期望之外的“记忆效应” # ✅ 正确:用 None 作为哨兵 def append_item(item, bucket=None): if bucket is None: bucket = [] bucket.append(item) return bucket

2) 圈复杂度控制与拆解

python
# ❌ 复杂、嵌套多 def handle(order): if order.valid: if order.paid: if order.stock_ok: ship(order) else: refund(order) else: notify_to_pay(order) # ✅ 早返回 + 小函数 def handle(order): if not order.valid: return reject(order) if not order.paid: return notify_to_pay(order) return _fulfill(order) def _fulfill(order): if not order.stock_ok: return refund(order) return ship(order)

3) 单一职责与命名

python
# ❌ 名不副实:既下载又解析又落库 def process_user_data(url): ... # ✅ 明确边界 def fetch_user_csv(url): ... def parse_user_csv(text: str) -> list[User]: ... def save_users(users: list[User]) -> None: ...

Python 模块导入规则

规则/场景要点
每行一个 importimport os / import sys,不要 import sys, os
import 位置顶部(在模块注释/文档串之后、模块常量之前)
分组顺序标准库 / 第三方 / 本地应用(每组之间空一行)
导入风格优先 绝对导入,在包内可使用显式相对导入(谨慎)
禁止from module import *(污染命名空间、影响静态分析)
允许为长名或约定重命名(import numpy as np
工具使用 isort / ruff / black 自动化排序与格式化

1. 导入位置:文件顶部

导入语句应放在模块文件顶部(模块注释 / 文档字符串之后),并在模块全局常量/变量之前。例如:

python
"""module docstring""" import os import typing CONSTANT = 42

2. 导入分组和顺序

把 imports 分成三组,每组内部按字母序排列(工具会自动处理):

  • 标准库(os, sys, json...)
  • 第三方库(requests, numpy...)
  • 本地应用/库(from mypkg import utils)
  • 每两组之间用一个空行分隔:
python
# 标准库 import os import sys # 第三方 import requests import numpy as np # 本地应用/库 from myapp.utils import helper

3. 导入的例外/特殊场景

虽然一般放顶部,但下列情形允许局部导入:

  • 避免循环依赖(circular import):把某个依赖移入函数内部,延迟导入。
  • 可选依赖:仅在某些功能执行时才需要,避免程序启动就报错。
  • 性能/启动时间优化:重型库(如 pandas)在仅部分路径使用时可延迟导入。
python
def use_pandas(): import pandas as pd # 延迟导入,避免全局开销或循环依赖 return pd.DataFrame(...)

注释

规则强制 / 推荐要点
接口注释(文档字符串)使用 """强制模块、类、公共函数/方法必须要有 docstring(接口注释)
代码块注释使用 #强制每行注释以 # 开始;每行 # 后跟 1 个空格
内联注释前至少两个空格强制(PEP8)例如:x = x + 1 # 增加计数# 前至少 2 个空格)
块注释独占一行并与代码对齐强制块注释通常与代码缩进对齐,不能放在代码行尾(除简短内联)
公共 / 重要函数必须有 docstring强制docstring 需包含简介,必要时说明参数/返回/异常
逻辑复杂处必须注释强制复杂算法、边界条件、hack/兼容性等必须写注释并链接 issue/PR
** TODO 注释**推荐使用 # TODO:,最好附 issue 编号和责任人
注释应说明“为什么”而不是重复“做什么”推荐注释解释设计动机/限制/副作用,而不是逐行解释可读代码
注释要随代码修改更新强制(流程)代码变动时同步更新注释;PR 必检项之一

1. Docstring(接口注释,使用 """)

用途:模块 / 类 / 函数 / 方法 的接口文档 — 描述做什么、参数、返回值、异常、示例等。 格式建议:首行一句话概述;若有更多说明,首行后空行写详细描述。

Google 风格

python
def find_user(user_id: int) -> dict | None: """Find a user by id. Args: user_id (int): The ID of the user to lookup. Returns: dict | None: The user dict if found, otherwise None. Raises: DatabaseError: If the DB query fails. """ ...

模块 docstring(文件顶部)

python
"""utils.strings Utilities for string normalization and tokenization used across the service. """

2. TODO / FIXME / HACK / NOTE

用途:标记待办、需要修复或不完美的解决方案。

标记含义典型用途示例
TODO待办事项代码中需要实现的功能、优化、改进或重构# TODO(issue#123): refactor to use X strategy — @alice
FIXME待修复表示当前代码有 bug、问题或不可靠,需要修复# FIXME: this workaround fails on Windows
HACK临时解决方案 / 权宜之计表示代码写得不够优雅或安全,是临时方案,需要优化或替换# HACK: using sleep to wait for API, should use event listener
NOTE说明 / 提示给阅读代码的人提供额外信息、设计思路或注意事项# NOTE: this value is hardcoded for compatibility with legacy system
python
def process_data(data): # TODO(issue#101): add input validation — @bob # FIXME: fails if data contains None # HACK: using global cache to speed up, need proper caching later # NOTE: this function is called by both sync and async tasks ...

在团队中统一格式,比如都加 issue# 和负责人,可以用 CI/脚本统计 TODO/FIXME/HACK 数量,避免遗漏。

真值判断

1. 容器类型空值判断(推荐使用布尔值而非长度比较)

类型空值布尔值推荐写法不推荐写法
列表 list[]Falseif not user_list: / if user_list:if len(user_list) == 0: / if len(user_list):
元组 tuple()Falseif not my_tuple: / if my_tuple:if len(my_tuple) == 0:
字典 dict{}Falseif not my_dict: / if my_dict:if len(my_dict) == 0:
集合 setset()Falseif not my_set: / if my_set:if len(my_set) == 0:
字符串 str''Falseif not my_str: / if my_str:if len(my_str) == 0:

理由:

  • Python 内置类型的空值都可以被视作 False,非空值视作 True。
  • if user_list: 比 if len(user_list) 更简洁,可读性高。
  • 避免不必要的函数调用(如 len()),性能上略优。

示例:

python
user_list = [] # 推荐 if not user_list: print("列表为空") if user_list: print("列表不为空") # 不推荐 if len(user_list) == 0: print("列表为空") if len(user_list): print("列表不为空")

2. None 值判断

判断方式推荐性示例理由
is None / is not None✅ 推荐if value is None: / if value is not None:显式判断,逻辑清晰,避免误判空字符串、0 或空列表
if not value is None❌ 不推荐if not value is None:冗余且可读性差
if value:⚠️ 小心只在你希望 None / 0 / '' / [] 都视作 False 时使用用于一般真值判断,不用于严格 None 判断

示例:

python
value = None # 推荐 if value is None: print("value 是 None") if value is not None: print("value 有值") # 不推荐 if not value is None: print("不要这样写")

3. 布尔类型判断

情况推荐写法不推荐写法理由
True / False 判断if greeting: / if not greeting:if greeting is True: / if greeting == True:Python 内置布尔类型可直接用作条件判断,冗余比较会降低可读性

示例:

python
greeting = True # 推荐 if greeting: print("Hello") # 不推荐 if greeting is True: print("Hello") if greeting == True: print("Hello")

4. 综合实例

python
user_list = [] greeting = True value = None # 容器判断 if not user_list: print("列表为空") # 布尔类型判断 if greeting: print("问候开启") # None 判断 if value is None: print("值未初始化") # 结合使用 if user_list and greeting and value is not None: print("执行逻辑")

异常捕捉

1. 异常捕捉原则

规则强制 / 推荐说明
捕获不可预知的异常强制如网络请求、文件读写、IO 操作可能失败的场景,使用 try-except 保证流程正常
异常捕获颗粒度应细强制尽量对单行或单操作捕获异常,避免将大段逻辑放在一个 try 块中
区分稳定代码与非稳定代码强制稳定代码指理论上不会出错的逻辑,如简单变量赋值,不建议放入 try
异常不能作为流程控制强制条件判断不要用异常来代替 if-else
捕获后必须处理或抛出强制禁止捕获异常后直接 pass,如果无法处理,应 raise 给调用者
finally 中禁止使用 return强制finally 中 return 会覆盖正常流程的返回值,导致逻辑混乱

2. 推荐做法

场景推荐写法说明
简化复杂嵌套逻辑使用 try-except 替代深层 if-else将易出错的操作放入 try,简化层层判断
可预检查推荐先用判断条件规避异常例如检查文件是否存在,再打开
错误返回推荐抛出异常类而非返回错误变量便于上层统一捕获和处理异常

3. 异常处理结构示例

单操作异常捕获(颗粒度小)

python
try: value = int(user_input) except ValueError as e: print(f"输入错误: {e}")

大段 try 块(不推荐)

python
# ❌ 错误示例 try: value = int(user_input) result = 10 / value with open("file.txt") as f: data = f.read() except Exception as e: print("出错了") # 不明确,不利于排查

finally 禁止 return

python
def test_finally(): try: return 1 finally: return 2 # ❌ 会覆盖 try 的返回值 print(test_finally()) # 输出 2,而非 1

推荐的 finally 用法

python
def test_finally_correct(): try: return 1 finally: print("执行清理操作") # 仅做清理,不 return print(test_finally_correct()) # 输出 1

项目工程结构

推荐项目结构示例如下:

markdown
sample_project ├── readme.md ├── docs │ ├── api.yml │ └── public_read.md ├── requirements.txt ├── app │ ├── __init__.py │ ├── core.py │ └── helpers.py ├── config │ ├── mysql.py ├── deploy │ ├── __init__.py │ └── run.sh ├── db │ ├── utils │ └── tests ├── __init__.py └── test_basic.py

1. 项目根目录文件

文件 / 目录说明建议
readme.md项目说明文件,通常包含项目简介、安装方法、使用方法、目录结构、贡献指南等必须存在,方便开发者快速了解项目
requirements.txt列出项目依赖的 Python 包及版本可以用 pip freeze > requirements.txt 或手动维护版本号
docs/存放项目文档,如功能说明、API 文档、设计文档可存放 Markdown、OpenAPI(api.yml)等文档,便于开发和运维
tests/单元测试目录,存放测试用例每个子模块建议对应一个测试文件,便于覆盖功能模块

2. 核心代码目录 app/

文件 / 目录说明建议
__init__.py标记 app 为包可为空,或初始化包级逻辑
core.py核心业务逻辑模块放主要功能代码
helpers.py工具函数、辅助方法避免把工具函数混入核心逻辑,提高可维护性

提示

核心目录 app/ 是业务逻辑的中心,建议按照功能模块拆分子目录,例如 app/user/、app/order/ 等。

3. 配置目录 config/

文件 / 目录说明建议
mysql.py存放数据库相关配置可以放不同环境配置,如 dev.pyprod.py,便于环境切换
redis.py(可选)存放 Redis、缓存等配置分离配置和业务逻辑,遵循 12-factor App 原则

提示

配置文件一般只存放静态配置或常量,不应包含业务逻辑。

4. 部署目录 deploy/

文件 / 目录说明建议
run.sh部署脚本,例如启动、初始化、迁移数据库可写成 shell 脚本、docker-compose 或 Ansible playbook
__init__.py如果需要作为包导入部署脚本通常可为空
其他可以放 docker-compose.yml、k8s 配置等便于统一部署管理

5. 数据库目录 db/

文件 / 目录说明建议
db/存放数据库相关代码、SQL 脚本或 ORM 模型可以拆分为 db/migrationsdb/models,便于管理

6. 工具目录 utils/

文件 / 目录说明建议
utils/存放项目通用工具函数、独立于业务逻辑的模块例如日志工具、数据转换函数、通用验证方法等
命名模块和函数命名清晰、简短避免和业务模块混淆

7. 测试目录 tests/

文件 / 目录说明建议
__init__.py标记为包可为空
test_basic.py测试文件每个模块/功能对应一个测试文件
测试风格使用 pytestunittest测试应覆盖核心逻辑,尽量保证 CI/CD 通过率

本文作者:精卫

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!