[{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/http/","section":"标签","summary":"","title":"HTTP","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/logging/","section":"标签","summary":"","title":"Logging","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/categories/python/","section":"Categories","summary":"","title":"Python","type":"categories"},{"content":"这里记录一些学习python的内容\n","date":"2026-04-22","externalUrl":null,"permalink":"/posts/python/","section":"文章","summary":"","title":"Python","type":"posts"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/python/","section":"标签","summary":"","title":"Python","type":"tags"},{"content":"在现代 Web 开发中，与 API 交互是必不可少的一项技能。无论是调用第三方接口获取数据，还是开发自己的后端服务，HTTP 请求都是连接客户端与服务端的桥梁。\nPython 的 requests 库是 Python 生态中最流行的 HTTP 客户端库，它以简洁优雅的 API 设计著称。本文将带你从入门到精通，全面掌握 requests 库的用法。\n为什么选择 requests？ # 在 requests 出现之前，Python 开发者通常使用 urllib 来发送 HTTP 请求。但 urllib 的 API 设计较为复杂，需要处理编码、会话管理、Cookie 等各种细节。requests 库的出现彻底改变了这一局面：\n简洁的 API：一行代码即可完成 GET 请求 自动编码处理：无需手动处理编码问题 会话管理：方便管理 Cookie 和会话 超时控制：防止请求无限等待 文件上传/下载：内置支持 代理支持：轻松配置代理服务器 # 安装 requests pip install requests 基础用法 # GET 请求 # GET 请求是最常用的 HTTP 方法，用于从服务器获取数据。\nimport requests # 最简单的 GET 请求 response = requests.get(\u0026#39;https://api.github.com\u0026#39;) # 查看响应状态码 print(response.status_code) # 200 # 查看响应内容 print(response.text) # 查看响应头 print(response.headers) # 查看 JSON 响应（如果服务器返回 JSON） data = response.json() print(data) 带参数的 GET 请求 # 有两种方式传递 URL 参数：\n# 方式一：直接拼接 URL response = requests.get(\u0026#39;https://api.github.com/search/repositories?q=python\u0026amp;sort=stars\u0026#39;) # 方式二：使用 params 参数（推荐，更清晰） params = { \u0026#39;q\u0026#39;: \u0026#39;python\u0026#39;, \u0026#39;sort\u0026#39;: \u0026#39;stars\u0026#39;, \u0026#39;order\u0026#39;: \u0026#39;desc\u0026#39; } response = requests.get(\u0026#39;https://api.github.com/search/repositories\u0026#39;, params=params) print(response.url) # 自动拼接成完整 URL POST 请求 # POST 请求用于向服务器提交数据。\n# 提交表单数据 data = { \u0026#39;username\u0026#39;: \u0026#39;test\u0026#39;, \u0026#39;password\u0026#39;: \u0026#39;123456\u0026#39; } response = requests.post(\u0026#39;https://httpbin.org/post\u0026#39;, data=data) print(response.json()) # 提交 JSON 数据 import json response = requests.post( \u0026#39;https://httpbin.org/post\u0026#39;, json={\u0026#39;name\u0026#39;: \u0026#39;张三\u0026#39;, \u0026#39;age\u0026#39;: 25} ) print(response.json()) 其他 HTTP 方法 # requests 支持所有标准的 HTTP 方法：\n# PUT 请求 - 更新资源 response = requests.put(\u0026#39;https://httpbin.org/put\u0026#39;, data={\u0026#39;key\u0026#39;: \u0026#39;value\u0026#39;}) # DELETE 请求 - 删除资源 response = requests.delete(\u0026#39;https://httpbin.org/delete\u0026#39;) # PATCH 请求 - 部分更新 response = requests.patch(\u0026#39;https://httpbin.org/patch\u0026#39;, data={\u0026#39;key\u0026#39;: \u0026#39;new_value\u0026#39;}) # HEAD 请求 - 只获取响应头 response = requests.head(\u0026#39;https://httpbin.org/get\u0026#39;) # OPTIONS 请求 - 获取支持的 HTTP 方法 response = requests.options(\u0026#39;https://httpbin.org/get\u0026#39;) print(response.headers.get(\u0026#39;allow\u0026#39;)) 响应对象详解 # 发送请求后，会返回一个 Response 对象，它包含了服务器的所有响应信息。\n常用属性 # response = requests.get(\u0026#39;https://httpbin.org/get\u0026#39;) # 状态码 print(response.status_code) # 200 # 判断状态码是否成功（2xx 表示成功） print(response.ok) # True # 响应内容 print(response.text) # 文本形式（自动解码） print(response.content) # 字节形式（二进制数据） # JSON 响应 print(response.json()) # 解析为 Python 对象 # 响应头 print(response.headers) # 所有响应头 print(response.headers[\u0026#39;Content-Type\u0026#39;]) # 特定响应头 # 请求信息 print(response.url) # 最终请求的 URL print(response.request) # 请求对象 状态码处理 # # 方式一：手动检查状态码 response = requests.get(\u0026#39;https://httpbin.org/get\u0026#39;) if response.status_code == 200: print(\u0026#39;请求成功\u0026#39;) elif response.status_code == 404: print(\u0026#39;资源不存在\u0026#39;) else: print(\u0026#39;其他错误\u0026#39;) # 方式二：使用 requests 内置的状态码常量 from requests import codes response = requests.get(\u0026#39;https://httpbin.org/get\u0026#39;) if response.status_code == codes.ok: # codes.ok == 200 print(\u0026#39;请求成功\u0026#39;) # 常见状态码常量 print(codes.ok) # 200 print(codes.created) # 201 print(codes.no_content) # 204 print(codes.not_found) # 404 print(codes.server_error) # 500-599 请求头处理 # 添加自定义请求头 # headers = { \u0026#39;User-Agent\u0026#39;: \u0026#39;MyApp/1.0\u0026#39;, \u0026#39;Accept\u0026#39;: \u0026#39;application/json\u0026#39;, \u0026#39;Authorization\u0026#39;: \u0026#39;Bearer your_token_here\u0026#39;, \u0026#39;X-Custom-Header\u0026#39;: \u0026#39;custom_value\u0026#39; } response = requests.get(\u0026#39;https://httpbin.org/headers\u0026#39;, headers=headers) print(response.json()) 查看响应的编码 # response = requests.get(\u0026#39;https://httpbin.org/encoding/utf8\u0026#39;) print(response.encoding) # 自动检测编码 # 手动设置编码 response.encoding = \u0026#39;utf-8\u0026#39; print(response.text) Cookie 处理 # 发送带 Cookie 的请求 # # 方式一：通过 headers 发送 cookies = {\u0026#39;session_id\u0026#39;: \u0026#39;abc123\u0026#39;} response = requests.get(\u0026#39;https://httpbin.org/cookies\u0026#39;, headers={\u0026#39;Cookie\u0026#39;: \u0026#39;session_id=abc123\u0026#39;}) # 方式二：使用 cookies 参数 response = requests.get(\u0026#39;https://httpbin.org/cookies\u0026#39;, cookies={\u0026#39;session_id\u0026#39;: \u0026#39;abc123\u0026#39;}) print(response.json()) # 设置多个 Cookie cookies = { \u0026#39;name\u0026#39;: \u0026#39;张三\u0026#39;, \u0026#39;age\u0026#39;: \u0026#39;25\u0026#39;, \u0026#39;city\u0026#39;: \u0026#39;北京\u0026#39; } response = requests.get(\u0026#39;https://httpbin.org/cookies\u0026#39;, cookies=cookies) 获取响应中的 Cookie # response = requests.get(\u0026#39;https://httpbin.org/cookies/set/name/value\u0026#39;) print(response.cookies) # 获取所有 Cookie print(response.cookies[\u0026#39;name\u0026#39;]) # 获取特定 Cookie 会话对象 # Session 对象允许你在多个请求之间保持某些参数和数据，如 Cookie、请求头等。\n为什么使用 Session？ # 普通请求每次都会创建新的连接，而 Session 会复用 TCP 连接，提高性能：\n# 普通方式 - 每次请求都是独立的 requests.get(\u0026#39;https://httpbin.org/cookies/set/session_id/abc123\u0026#39;) requests.get(\u0026#39;https://httpbin.org/cookies\u0026#39;) # 无法获取到上面的 Cookie # 使用 Session - Cookie 会自动保持 session = requests.Session() session.get(\u0026#39;https://httpbin.org/cookies/set/session_id/abc123\u0026#39;) response = session.get(\u0026#39;https://httpbin.org/cookies\u0026#39;) print(response.json()) # {\u0026#39;cookies\u0026#39;: {\u0026#39;session_id\u0026#39;: \u0026#39;abc123\u0026#39;}} Session 的实际应用 # # 模拟登录并保持会话 session = requests.Session() # 1. 登录获取 Cookie login_data = { \u0026#39;username\u0026#39;: \u0026#39;your_username\u0026#39;, \u0026#39;password\u0026#39;: \u0026#39;your_password\u0026#39; } session.post(\u0026#39;https://example.com/login\u0026#39;, data=login_data) # 2. 后续请求自动带上 Cookie response = session.get(\u0026#39;https://example.com/profile\u0026#39;) print(response.text) # 3. 设置默认请求头（所有请求都会带上） session.headers.update({ \u0026#39;User-Agent\u0026#39;: \u0026#39;MyApp/1.0\u0026#39;, \u0026#39;Accept\u0026#39;: \u0026#39;application/json\u0026#39; }) # 4. 清理会话 session.close() 超时与重试 # 设置超时 # 超时设置非常重要，可以防止请求无限等待导致程序卡死：\n# 设置超时时间（秒） response = requests.get(\u0026#39;https://httpbin.org/delay/1\u0026#39;, timeout=5) # 5秒超时 # 分别设置连接超时和读取超时 response = requests.get( \u0026#39;https://httpbin.org/delay/2\u0026#39;, timeout=(3, 10) # (连接超时, 读取超时) ) # 不设置超时可能导致程序卡死 # response = requests.get(\u0026#39;https://httpbin.org/delay/10\u0026#39;) # 危险！ 超时异常处理 # from requests.exceptions import Timeout, RequestException try: response = requests.get(\u0026#39;https://httpbin.org/delay/10\u0026#39;, timeout=5) print(response.json()) except Timeout: print(\u0026#39;请求超时，服务器响应太慢\u0026#39;) except RequestException as e: print(f\u0026#39;请求失败: {e}\u0026#39;) 配置重试 # from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry def create_retry_session(retries=3, backoff_factor=0.5): \u0026#34;\u0026#34;\u0026#34;创建一个带有重试机制的 Session\u0026#34;\u0026#34;\u0026#34; session = requests.Session() retry = Retry( total=retries, # 总重试次数 read=retries, # 读取重试次数 connect=retries, # 连接重试次数 backoff_factor=backoff_factor, # 重试间隔倍数 status_forcelist=[500, 502, 503, 504] # 遇到这些状态码时重试 ) adapter = HTTPAdapter(max_retries=retry) session.mount(\u0026#39;http://\u0026#39;, adapter) session.mount(\u0026#39;https://\u0026#39;, adapter) return session # 使用 session = create_retry_session() response = session.get(\u0026#39;https://httpbin.org/status/500\u0026#39;) 错误处理 # requests 库定义了多种异常类型：\nfrom requests.exceptions import ( ConnectionError, # 网络连接错误 HTTPError, # HTTP 错误响应 Timeout, # 请求超时 TooManyRedirects, # 重定向次数过多 RequestException # 所有异常的基类 ) try: response = requests.get(\u0026#39;https://httpbin.org/get\u0026#39;) response.raise_for_status() # 如果状态码不是 200，抛出异常 except HTTPError as e: print(f\u0026#39;HTTP 错误: {e.response.status_code}\u0026#39;) except ConnectionError: print(\u0026#39;网络连接失败\u0026#39;) except Timeout: print(\u0026#39;请求超时\u0026#39;) except TooManyRedirects: print(\u0026#39;重定向次数过多\u0026#39;) except RequestException as e: print(f\u0026#39;请求失败: {e}\u0026#39;) 使用 raise_for_status() # response = requests.get(\u0026#39;https://httpbin.org/status/404\u0026#39;) try: response.raise_for_status() except HTTPError as e: print(f\u0026#39;错误: {e}\u0026#39;) print(f\u0026#39;响应内容: {response.text}\u0026#39;) 文件上传与下载 # 上传文件 # # 上传文件 files = { \u0026#39;file\u0026#39;: open(\u0026#39;example.txt\u0026#39;, \u0026#39;rb\u0026#39;) } response = requests.post(\u0026#39;https://httpbin.org/post\u0026#39;, files=files) # 指定文件名和内容类型 files = { \u0026#39;file\u0026#39;: (\u0026#39;custom_name.txt\u0026#39;, open(\u0026#39;example.txt\u0026#39;, \u0026#39;rb\u0026#39;), \u0026#39;text/plain\u0026#39;) } response = requests.post(\u0026#39;https://httpbin.org/post\u0026#39;, files=files) # 上传图片 files = { \u0026#39;image\u0026#39;: (\u0026#39;photo.jpg\u0026#39;, open(\u0026#39;photo.jpg\u0026#39;, \u0026#39;rb\u0026#39;), \u0026#39;image/jpeg\u0026#39;) } response = requests.post(\u0026#39;https://httpbin.org/post\u0026#39;, files=files) # 上传多个文件 files = [ (\u0026#39;images\u0026#39;, (\u0026#39;img1.jpg\u0026#39;, open(\u0026#39;img1.jpg\u0026#39;, \u0026#39;rb\u0026#39;), \u0026#39;image/jpeg\u0026#39;)), (\u0026#39;images\u0026#39;, (\u0026#39;img2.jpg\u0026#39;, open(\u0026#39;img2.jpg\u0026#39;, \u0026#39;rb\u0026#39;), \u0026#39;image/jpeg\u0026#39;)) ] response = requests.post(\u0026#39;https://httpbin.org/post\u0026#39;, files=files) 下载文件 # # 下载小文件 response = requests.get(\u0026#39;https://example.com/image.jpg\u0026#39;) with open(\u0026#39;image.jpg\u0026#39;, \u0026#39;wb\u0026#39;) as f: f.write(response.content) # 下载大文件（流式下载） response = requests.get(\u0026#39;https://example.com/large_file.zip\u0026#39;, stream=True) with open(\u0026#39;large_file.zip\u0026#39;, \u0026#39;wb\u0026#39;) as f: for chunk in response.iter_content(chunk_size=1024): if chunk: f.write(chunk) # 使用迭代器下载 with requests.get(\u0026#39;https://example.com/image.jpg\u0026#39;, stream=True) as response: with open(\u0026#39;image.jpg\u0026#39;, \u0026#39;wb\u0026#39;) as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) 下载进度显示 # import os def download_with_progress(url, filename): \u0026#34;\u0026#34;\u0026#34;带进度显示的下载函数\u0026#34;\u0026#34;\u0026#34; response = requests.get(url, stream=True) total_size = int(response.headers.get(\u0026#39;content-length\u0026#39;, 0)) downloaded = 0 with open(filename, \u0026#39;wb\u0026#39;) as f: for chunk in response.iter_content(chunk_size=1024): if chunk: f.write(chunk) downloaded += len(chunk) progress = (downloaded / total_size) * 100 print(f\u0026#39;\\r下载进度: {progress:.1f}%\u0026#39;, end=\u0026#39;\u0026#39;) print(f\u0026#39;\\n文件已保存到: {filename}\u0026#39;) # 使用 download_with_progress( \u0026#39;https://example.com/large_file.zip\u0026#39;, \u0026#39;large_file.zip\u0026#39; ) SSL 证书验证 # 忽略 SSL 证书验证（仅用于测试） # # 警告：生产环境不要这样做！ response = requests.get(\u0026#39;https://example.com\u0026#39;, verify=False) # 消除警告 import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) response = requests.get(\u0026#39;https://example.com\u0026#39;, verify=False) 指定 CA 证书 # # 使用自定义 CA 证书 response = requests.get(\u0026#39;https://example.com\u0026#39;, verify=\u0026#39;/path/to/ca-bundle.crt\u0026#39;) # 使用系统默认 CA 证书 import certifi response = requests.get(\u0026#39;https://example.com\u0026#39;, verify=certifi.where()) 代理设置 # HTTP 代理 # # 普通代理 proxies = { \u0026#39;http\u0026#39;: \u0026#39;http://proxy.example.com:8080\u0026#39;, \u0026#39;https\u0026#39;: \u0026#39;http://proxy.example.com:8080\u0026#39; } response = requests.get(\u0026#39;https://httpbin.org/ip\u0026#39;, proxies=proxies) # 带认证的代理 proxies = { \u0026#39;http\u0026#39;: \u0026#39;http://user:password@proxy.example.com:8080\u0026#39;, \u0026#39;https\u0026#39;: \u0026#39;http://user:password@proxy.example.com:8080\u0026#39; } response = requests.get(\u0026#39;https://httpbin.org/ip\u0026#39;, proxies=proxies) 在 Session 中使用代理 # session = requests.Session() session.proxies = { \u0026#39;http\u0026#39;: \u0026#39;http://proxy.example.com:8080\u0026#39;, \u0026#39;https\u0026#39;: \u0026#39;http://proxy.example.com:8080\u0026#39; } response = session.get(\u0026#39;https://httpbin.org/ip\u0026#39;) 身份认证 # 基本认证 # from requests.auth import HTTPBasicAuth response = requests.get( \u0026#39;https://api.github.com/user\u0026#39;, auth=HTTPBasicAuth(\u0026#39;username\u0026#39;, \u0026#39;password\u0026#39;) ) # 简写形式 response = requests.get(\u0026#39;https://api.github.com/user\u0026#39;, auth=(\u0026#39;username\u0026#39;, \u0026#39;password\u0026#39;)) 其他认证方式 # from requests.auth import HTTPBasicAuth, HTTPDigestAuth # Digest 认证 response = requests.get(\u0026#39;https://httpbin.org/digest-auth/auth/user/passwd\u0026#39;, auth=HTTPDigestAuth(\u0026#39;user\u0026#39;, \u0026#39;passwd\u0026#39;)) # OAuth 认证 headers = { \u0026#39;Authorization\u0026#39;: \u0026#39;Bearer your_oauth_token\u0026#39; } response = requests.get(\u0026#39;https://api.example.com/protected\u0026#39;, headers=headers) 实战案例 # 调用 GitHub API # import requests class GitHubAPI: \u0026#34;\u0026#34;\u0026#34;GitHub API 封装\u0026#34;\u0026#34;\u0026#34; def __init__(self, token=None): self.base_url = \u0026#39;https://api.github.com\u0026#39; self.headers = { \u0026#39;Accept\u0026#39;: \u0026#39;application/vnd.github.v3+json\u0026#39; } if token: self.headers[\u0026#39;Authorization\u0026#39;] = f\u0026#39;token {token}\u0026#39; def get_user(self, username): \u0026#34;\u0026#34;\u0026#34;获取用户信息\u0026#34;\u0026#34;\u0026#34; response = requests.get( f\u0026#39;{self.base_url}/users/{username}\u0026#39;, headers=self.headers ) response.raise_for_status() return response.json() def get_repos(self, username, sort=\u0026#39;updated\u0026#39;): \u0026#34;\u0026#34;\u0026#34;获取用户的仓库列表\u0026#34;\u0026#34;\u0026#34; response = requests.get( f\u0026#39;{self.base_url}/users/{username}/repos\u0026#39;, headers=self.headers, params={\u0026#39;sort\u0026#39;: sort, \u0026#39;per_page\u0026#39;: 10} ) response.raise_for_status() return response.json() def create_issue(self, owner, repo, title, body): \u0026#34;\u0026#34;\u0026#34;创建 Issue\u0026#34;\u0026#34;\u0026#34; url = f\u0026#39;{self.base_url}/repos/{owner}/{repo}/issues\u0026#39; data = {\u0026#39;title\u0026#39;: title, \u0026#39;body\u0026#39;: body} response = requests.post(url, headers=self.headers, json=data) response.raise_for_status() return response.json() # 使用 github = GitHubAPI(token=\u0026#39;your_token_here\u0026#39;) user_info = github.get_user(\u0026#39;Hungerdream\u0026#39;) print(f\u0026#34;用户名: {user_info[\u0026#39;name\u0026#39;]}\u0026#34;) print(f\u0026#34;仓库数: {user_info[\u0026#39;public_repos\u0026#39;]}\u0026#34;) repos = github.get_repos(\u0026#39;Hungerdream\u0026#39;) for repo in repos[:5]: print(f\u0026#34;- {repo[\u0026#39;name\u0026#39;]}: ⭐ {repo[\u0026#39;stargazers_count\u0026#39;]}\u0026#34;) 批量下载图片 # import requests import os from concurrent.futures import ThreadPoolExecutor, as_completed def download_image(url, save_path): \u0026#34;\u0026#34;\u0026#34;下载单张图片\u0026#34;\u0026#34;\u0026#34; try: response = requests.get(url, timeout=10) response.raise_for_status() # 从 URL 提取文件名 filename = url.split(\u0026#39;/\u0026#39;)[-1] filepath = os.path.join(save_path, filename) with open(filepath, \u0026#39;wb\u0026#39;) as f: f.write(response.content) return f\u0026#39;成功: {filename}\u0026#39; except Exception as e: return f\u0026#39;失败: {url} - {e}\u0026#39; def batch_download(urls, save_path, max_workers=5): \u0026#34;\u0026#34;\u0026#34;批量下载图片\u0026#34;\u0026#34;\u0026#34; os.makedirs(save_path, exist_ok=True) with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = {executor.submit(download_image, url, save_path): url for url in urls} for future in as_completed(futures): print(future.result()) # 使用 image_urls = [ \u0026#39;https://example.com/image1.jpg\u0026#39;, \u0026#39;https://example.com/image2.jpg\u0026#39;, \u0026#39;https://example.com/image3.jpg\u0026#39;, ] batch_download(image_urls, \u0026#39;downloads/\u0026#39;) 最佳实践 # 1. 使用 context manager 管理资源 # # 确保连接被正确关闭 with requests.Session() as session: response = session.get(\u0026#39;https://api.example.com/data\u0026#39;) data = response.json() 2. 始终设置超时 # response = requests.get(url, timeout=10) # 始终设置合理超时 3. 异常处理 # try: response = requests.get(url, timeout=10) response.raise_for_status() except requests.RequestException as e: logger.error(f\u0026#39;请求失败: {e}\u0026#39;) raise 4. 复用 Session # # 不要这样写 for url in urls: response = requests.get(url) # 每次创建新连接 # 应该这样写 with requests.Session() as session: for url in urls: response = session.get(url) # 复用连接 5. 不要禁用 SSL 验证 # # 危险！ response = requests.get(url, verify=False) # 如果必须使用自签名证书 import certifi response = requests.get(url, verify=certifi.where()) 总结 # requests 库是 Python 中处理 HTTP 请求的最佳选择。它的设计哲学是\u0026quot;让 HTTP 请求变得简单\u0026quot;，而它确实做到了这一点。\n本文涵盖了 requests 库的绝大部分功能：\n✅ GET/POST 等 HTTP 方法 ✅ 请求头、Cookie、会话管理 ✅ 超时、重试、错误处理 ✅ 文件上传下载 ✅ SSL 证书、代理设置 ✅ 各种认证方式 ✅ 实战案例和最佳实践 掌握这些内容，你就能应对绝大多数与 HTTP 相关的开发需求了。\n📚 推荐阅读\n官方文档 HTTP 方法详解 RESTful API 设计指南 ","date":"2026-04-22","externalUrl":null,"permalink":"/posts/python/python-requests-guide/","section":"文章","summary":"","title":"Python HTTP 请求完全指南：requests 库详解","type":"posts"},{"content":"Python 以其简洁优雅的语法著称，而列表推导式（List Comprehension）和生成器（Generator）是 Pythonic 编程风格的典型代表。掌握这些技巧，不仅能让你的代码更简洁，还能显著提升性能。\n本文将深入讲解列表推导式、集合推导式、字典推导式以及生成器的用法和最佳实践。\n什么是列表推导式？ # 列表推导式是一种简洁创建列表的方式。它的基本语法是：\n[表达式 for 变量 in 可迭代对象] 传统方式 vs 列表推导式 # # 传统方式 - 创建 0-9 的平方列表 squares = [] for i in range(10): squares.append(i ** 2) print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # 列表推导式 - 一行搞定 squares = [i ** 2 for i in range(10)] print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 性能对比 # import timeit # 传统循环 def traditional(): result = [] for i in range(1000): result.append(i ** 2) return result # 列表推导式 def comprehension(): return [i ** 2 for i in range(1000)] # 测试性能 t1 = timeit.timeit(traditional, number=1000) t2 = timeit.timeit(comprehension, number=1000) print(f\u0026#34;传统循环: {t1:.4f}秒\u0026#34;) print(f\u0026#34;列表推导式: {t2:.4f}秒\u0026#34;) print(f\u0026#34;列表推导式快 {(t1-t2)/t1*100:.1f}%\u0026#34;) 基础列表推导式 # 简单示例 # # 基本语法 numbers = [1, 2, 3, 4, 5] # 每个元素乘以 2 doubled = [x * 2 for x in numbers] print(doubled) # [2, 4, 6, 8, 10] # 字符串处理 words = [\u0026#39;hello\u0026#39;, \u0026#39;world\u0026#39;, \u0026#39;python\u0026#39;] upper_words = [word.upper() for word in words] print(upper_words) # [\u0026#39;HELLO\u0026#39;, \u0026#39;WORLD\u0026#39;, \u0026#39;PYTHON\u0026#39;] # 数学运算 numbers = [1, 4, 9, 16, 25] roots = [n ** 0.5 for n in numbers] print(roots) # [1.0, 2.0, 3.0, 4.0, 5.0] 处理各种数据结构 # # 处理元组 coordinates = [(1, 2), (3, 4), (5, 6)] x_coords = [x for x, y in coordinates] y_coords = [y for x, y in coordinates] print(x_coords) # [1, 3, 5] print(y_coords) # [2, 4, 6] # 处理字典 students = {\u0026#39;Alice\u0026#39;: 90, \u0026#39;Bob\u0026#39;: 85, \u0026#39;Charlie\u0026#39;: 92} names = [name for name in students.keys()] scores = [score for score in students.values()] print(names) # [\u0026#39;Alice\u0026#39;, \u0026#39;Bob\u0026#39;, \u0026#39;Charlie\u0026#39;] print(scores) # [90, 85, 92] # 处理字符串 sentence = \u0026#34;Python is awesome\u0026#34; letters = [char for char in sentence] print(letters) # [\u0026#39;P\u0026#39;, \u0026#39;y\u0026#39;, \u0026#39;t\u0026#39;, \u0026#39;h\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39;n\u0026#39;, \u0026#39; \u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;s\u0026#39;, \u0026#39; \u0026#39;, \u0026#39;a\u0026#39;, \u0026#39;w\u0026#39;, \u0026#39;e\u0026#39;, \u0026#39;s\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39;m\u0026#39;, \u0026#39;e\u0026#39;] 带条件的列表推导式 # 只包含满足条件的元素 # numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 只保留偶数 even_numbers = [x for x in numbers if x % 2 == 0] print(even_numbers) # [2, 4, 6, 8, 10] # 只保留大于 5 的数 large_numbers = [x for x in numbers if x \u0026gt; 5] print(large_numbers) # [6, 7, 8, 9, 10] # 只保留平方是偶数的数 numbers = [1, 2, 3, 4, 5] result = [x**2 for x in numbers if x**2 % 2 == 0] print(result) # [4, 16] 多重条件 # # and 条件 numbers = range(1, 21) result = [x for x in numbers if x \u0026gt; 5 if x \u0026lt; 15 if x % 2 == 0] print(result) # [6, 8, 10, 12, 14] # 等同于 result = [x for x in numbers if x \u0026gt; 5 and x \u0026lt; 15 and x % 2 == 0] print(result) # [6, 8, 10, 12, 14] if-else 表达式 # # 在表达式中使用条件（三元表达式） numbers = [1, 2, 3, 4, 5] # 如果是偶数则乘以 2，否则保持原值 result = [x * 2 if x % 2 == 0 else x for x in numbers] print(result) # [1, 4, 3, 8, 5] # 将数字分类 result = [\u0026#39;偶数\u0026#39; if x % 2 == 0 else \u0026#39;奇数\u0026#39; for x in numbers] print(result) # [\u0026#39;奇数\u0026#39;, \u0026#39;偶数\u0026#39;, \u0026#39;奇数\u0026#39;, \u0026#39;偶数\u0026#39;, \u0026#39;奇数\u0026#39;] 嵌套循环 # 扁平化嵌套列表 # # 二维列表扁平化 matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] flat = [num for row in matrix for num in row] print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9] # 传统方式 flat = [] for row in matrix: for num in row: flat.append(num) 生成所有组合 # # 扑克牌示例 ranks = [\u0026#39;A\u0026#39;, \u0026#39;2\u0026#39;, \u0026#39;3\u0026#39;, \u0026#39;4\u0026#39;, \u0026#39;5\u0026#39;, \u0026#39;6\u0026#39;, \u0026#39;7\u0026#39;, \u0026#39;8\u0026#39;, \u0026#39;9\u0026#39;, \u0026#39;10\u0026#39;, \u0026#39;J\u0026#39;, \u0026#39;Q\u0026#39;, \u0026#39;K\u0026#39;] suits = [\u0026#39;♠\u0026#39;, \u0026#39;♥\u0026#39;, \u0026#39;♦\u0026#39;, \u0026#39;♣\u0026#39;] cards = [f\u0026#34;{rank}{suit}\u0026#34; for rank in ranks for suit in suits] print(cards[:5]) # [\u0026#39;A♠\u0026#39;, \u0026#39;A♥\u0026#39;, \u0026#39;A♦\u0026#39;, \u0026#39;A♣\u0026#39;, \u0026#39;2♠\u0026#39;] # 坐标组合 x = [1, 2, 3] y = [4, 5] points = [(xi, yi) for xi in x for yi in y] print(points) # [(1, 4), (1, 5), (2, 4), (2, 5), (3, 4), (3, 5)] 集合推导式 # 集合推导式语法与列表推导式类似，但使用花括号 {}，且结果自动去重。\n# 基础集合推导式 numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] # 平方后放入集合（自动去重） squares = {x ** 2 for x in numbers} print(squares) # {1, 4, 9, 16} # 从字符串提取唯一字符 text = \u0026#34;hello world\u0026#34; unique_chars = {char for char in text if char != \u0026#39; \u0026#39;} print(unique_chars) # {\u0026#39;h\u0026#39;, \u0026#39;e\u0026#39;, \u0026#39;l\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39;w\u0026#39;, \u0026#39;r\u0026#39;, \u0026#39;d\u0026#39;} # 带条件的集合推导式 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 偶数的平方 even_squares = {x**2 for x in numbers if x % 2 == 0} print(even_squares) # {4, 16, 36, 64, 100} 集合运算 # # 使用集合推导式进行集合运算 a = [1, 2, 3, 4, 5] b = [4, 5, 6, 7, 8] # 交集 intersection = {x for x in a if x in b} print(intersection) # {4, 5} # 并集 union = {x for x in a}.union({x for x in b}) print(union) # {1, 2, 3, 4, 5, 6, 7, 8} # 差集 diff = {x for x in a if x not in b} print(diff) # {1, 2, 3} 字典推导式 # 字典推导式使用 {} 和冒号 :，可以快速创建字典。\n基础字典推导式 # # 基础语法：{key: value for ...} # 数字到平方的映射 numbers = [1, 2, 3, 4, 5] square_dict = {x: x ** 2 for x in numbers} print(square_dict) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25} # 字符串到长度的映射 words = [\u0026#39;apple\u0026#39;, \u0026#39;banana\u0026#39;, \u0026#39;cherry\u0026#39;] word_lengths = {word: len(word) for word in words} print(word_lengths) # {\u0026#39;apple\u0026#39;: 5, \u0026#39;banana\u0026#39;: 6, \u0026#39;cherry\u0026#39;: 6} # 反转字典（值作为键，键作为值） original = {\u0026#39;a\u0026#39;: 1, \u0026#39;b\u0026#39;: 2, \u0026#39;c\u0026#39;: 3} reversed_dict = {v: k for k, v in original.items()} print(reversed_dict) # {1: \u0026#39;a\u0026#39;, 2: \u0026#39;b\u0026#39;, 3: \u0026#39;c\u0026#39;} 带条件的字典推导式 # # 只包含满足条件的键值对 students = {\u0026#39;Alice\u0026#39;: 90, \u0026#39;Bob\u0026#39;: 75, \u0026#39;Charlie\u0026#39;: 85, \u0026#39;David\u0026#39;: 95} # 只保留分数大于等于 80 的学生 passed = {name: score for name, score in students.items() if score \u0026gt;= 80} print(passed) # {\u0026#39;Alice\u0026#39;: 90, \u0026#39;Charlie\u0026#39;: 85, \u0026#39;David\u0026#39;: 95} # 基于条件转换值 products = {\u0026#39;apple\u0026#39;: 5, \u0026#39;banana\u0026#39;: 3, \u0026#39;cherry\u0026#39;: 10, \u0026#39;date\u0026#39;: 7} # 价格翻倍 doubled_prices = {name: price * 2 for name, price in products.items()} print(doubled_prices) # {\u0026#39;apple\u0026#39;: 10, \u0026#39;banana\u0026#39;: 6, \u0026#39;cherry\u0026#39;: 20, \u0026#39;date\u0026#39;: 14} # 价格大于 5 的商品打 8 折 discounted = {name: price * 0.8 for name, price in products.items() if price \u0026gt; 5} print(discounted) # {\u0026#39;apple\u0026#39;: 4.0, \u0026#39;cherry\u0026#39;: 8.0, \u0026#39;date\u0026#39;: 5.6} 复杂字典推导式 # # 两个列表组合成字典 keys = [\u0026#39;name\u0026#39;, \u0026#39;age\u0026#39;, \u0026#39;city\u0026#39;] values = [\u0026#39;Alice\u0026#39;, 25, \u0026#39;Beijing\u0026#39;] # 方法一：zip info = {k: v for k, v in zip(keys, values)} print(info) # {\u0026#39;name\u0026#39;: \u0026#39;Alice\u0026#39;, \u0026#39;age\u0026#39;: 25, \u0026#39;city\u0026#39;: \u0026#39;Beijing\u0026#39;} # 方法二：索引 info = {keys[i]: values[i] for i in range(len(keys))} print(info) # {\u0026#39;name\u0026#39;: \u0026#39;Alice\u0026#39;, \u0026#39;age\u0026#39;: 25, \u0026#39;city\u0026#39;: \u0026#39;Beijing\u0026#39;} # 创建嵌套字典 students = [\u0026#39;Alice\u0026#39;, \u0026#39;Bob\u0026#39;, \u0026#39;Charlie\u0026#39;] grades = [90, 85, 92] student_grades = {student: {\u0026#39;grade\u0026#39;: grade, \u0026#39;status\u0026#39;: \u0026#39;passed\u0026#39; if grade \u0026gt;= 60 else \u0026#39;failed\u0026#39;} for student, grade in zip(students, grades)} print(student_grades) # {\u0026#39;Alice\u0026#39;: {\u0026#39;grade\u0026#39;: 90, \u0026#39;status\u0026#39;: \u0026#39;passed\u0026#39;}, # \u0026#39;Bob\u0026#39;: {\u0026#39;grade\u0026#39;: 85, \u0026#39;status\u0026#39;: \u0026#39;passed\u0026#39;}, # \u0026#39;Charlie\u0026#39;: {\u0026#39;grade\u0026#39;: 92, \u0026#39;status\u0026#39;: \u0026#39;passed\u0026#39;}} 生成器表达式 # 生成器表达式与列表推导式语法类似，但使用圆括号 () 而不是方括号 []。\n为什么要用生成器？ # import sys # 列表：一次性生成所有元素，占用内存 list_comp = [x ** 2 for x in range(1000000)] print(f\u0026#34;列表大小: {sys.getsizeof(list_comp)} bytes\u0026#34;) # 约 8MB # 生成器：按需生成，不占内存 gen_exp = (x ** 2 for x in range(1000000)) print(f\u0026#34;生成器大小: {sys.getsizeof(gen_exp)} bytes\u0026#34;) # 约 200 bytes 生成器表达式 # # 语法： (表达式 for 变量 in 可迭代对象) # 创建生成器 gen = (x ** 2 for x in range(5)) print(gen) # \u0026lt;generator object \u0026lt;genexpr\u0026gt; at 0x...\u0026gt; # 使用 next() 获取元素 print(next(gen)) # 0 print(next(gen)) # 1 print(next(gen)) # 4 # 遍历生成器 gen = (x ** 2 for x in range(5)) for value in gen: print(value) # 0 # 1 # 4 # 9 # 16 # 转换为列表（会占用内存） gen = (x ** 2 for x in range(5)) result = list(gen) print(result) # [0, 1, 4, 9, 16] 生成器 vs 列表推导式 # import timeit # 列表推导式：返回完整列表 def using_list(): return [x ** 2 for x in range(1000000)] # 生成器表达式：返回生成器对象 def using_generator(): return (x ** 2 for x in range(1000000)) t1 = timeit.timeit(using_list, number=1) t2 = timeit.timeit(using_generator, number=1) print(f\u0026#34;列表: {t1:.4f}秒\u0026#34;) print(f\u0026#34;生成器: {t2:.6f}秒\u0026#34;) # 几乎瞬间完成 生成器函数 # 生成器函数使用 yield 关键字，可以产生多个值。\n基础生成器函数 # def count_up_to(n): \u0026#34;\u0026#34;\u0026#34;计数生成器\u0026#34;\u0026#34;\u0026#34; count = 1 while count \u0026lt;= n: yield count count += 1 # 使用 counter = count_up_to(5) print(next(counter)) # 1 print(next(counter)) # 2 print(next(counter)) # 3 # 或者遍历 for num in count_up_to(5): print(num) # 1 # 2 # 3 # 4 # 5 yield 与 return # def with_return(n): \u0026#34;\u0026#34;\u0026#34;使用 return - 函数结束\u0026#34;\u0026#34;\u0026#34; for i in range(n): return i # 只返回第一个值 def with_yield(n): \u0026#34;\u0026#34;\u0026#34;使用 yield - 函数变成生成器\u0026#34;\u0026#34;\u0026#34; for i in range(n): yield i # 每次返回一个值，函数暂停 # return 版本 result = with_return(5) print(result) # 0 # yield 版本 result = with_yield(5) print(result) # \u0026lt;generator object\u0026gt; print(list(result)) # [0, 1, 2, 3, 4] 无限生成器 # # 无限序列生成器 def fibonacci(): \u0026#34;\u0026#34;\u0026#34;斐波那契数列生成器\u0026#34;\u0026#34;\u0026#34; a, b = 0, 1 while True: yield a a, b = b, a + b # 只获取前 10 个 fib = fibonacci() for _ in range(10): print(next(fib)) # 0 # 1 # 1 # 2 # 3 # 5 # 8 # 13 # 21 # 34 def infinite_range(start=0, step=1): \u0026#34;\u0026#34;\u0026#34;无限整数序列\u0026#34;\u0026#34;\u0026#34; while True: yield start start += step # 偶数序列 evens = infinite_range(step=2) for _ in range(10): print(next(evens)) # 0 # 2 # 4 # 6 # 8 # 10 # 12 # 14 # 16 # 18 生成器的高级用法 # def find_lines(filename): \u0026#34;\u0026#34;\u0026#34;逐行读取大文件\u0026#34;\u0026#34;\u0026#34; with open(filename, \u0026#39;r\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as f: for line in f: yield line.strip() # 使用 for line in find_lines(\u0026#39;huge_file.txt\u0026#39;): process(line) # 每次只处理一行，不会把整个文件加载到内存 def batch_process(items, batch_size=100): \u0026#34;\u0026#34;\u0026#34;批量处理数据\u0026#34;\u0026#34;\u0026#34; batch = [] for item in items: batch.append(item) if len(batch) \u0026gt;= batch_size: yield batch batch = [] if batch: yield batch # 使用 for batch in batch_process(range(250), batch_size=100): print(f\u0026#34;处理批次: {batch}\u0026#34;) # 处理批次: [0, 1, 2, ..., 99] # 处理批次: [100, 101, 102, ..., 199] # 处理批次: [200, 201, 202, ..., 249] 生成器的状态 # 生成器有三种状态方法：\ndef simple_gen(): yield 1 yield 2 yield 3 gen = simple_gen() print(next(gen)) # 1 print(next(gen)) # 2 print(next(gen)) # 3 try: print(next(gen)) # 抛出 StopIteration except StopIteration: print(\u0026#34;生成器耗尽\u0026#34;) # 重新使用生成器 gen = simple_gen() result = list(gen) # 消耗所有元素 print(result) # [1, 2, 3] # 再次使用 gen = simple_gen() print(next(gen, \u0026#39;默认值\u0026#39;)) # 1 - 有默认值，不会抛出异常 print(next(gen, \u0026#39;默认值\u0026#39;)) # 2 print(next(gen, \u0026#39;默认值\u0026#39;)) # 3 print(next(gen, \u0026#39;默认值\u0026#39;)) # 默认值 - 提供默认值 yield from # yield from 用于委托给子生成器：\ndef gen1(): yield 1 yield 2 def gen2(): yield 3 yield 4 # 传统方式 def combined_traditional(): for item in gen1(): yield item for item in gen2(): yield item # 使用 yield from def combined_yield_from(): yield from gen1() yield from gen2() # 测试 print(list(combined_yield_from())) # [1, 2, 3, 4] # 扁平化嵌套列表 def flatten(nested): for item in nested: if isinstance(item, (list, tuple)): yield from flatten(item) # 递归展平 else: yield item nested = [1, [2, 3], [4, [5, 6]], 7] print(list(flatten(nested))) # [1, 2, 3, 4, 5, 6, 7] 实用案例 # 1. 读取大文件 # def read_large_file(filepath, chunk_size=1024): \u0026#34;\u0026#34;\u0026#34;分块读取大文件\u0026#34;\u0026#34;\u0026#34; with open(filepath, \u0026#39;rb\u0026#39;) as f: while True: chunk = f.read(chunk_size) if not chunk: break yield chunk # 使用示例 for chunk in read_large_file(\u0026#39;huge_file.bin\u0026#39;, chunk_size=8192): process(chunk) 2. 实时数据处理 # def stock_price_stream(): \u0026#34;\u0026#34;\u0026#34;模拟股票价格流\u0026#34;\u0026#34;\u0026#34; import random price = 100 while True: price += random.uniform(-2, 2) yield price # 获取价格突破阈值的事件 def price_alerts(threshold): price_gen = stock_price_stream() for price in price_gen: if abs(price - 100) \u0026gt; threshold: yield price, abs(price - 100) for price, deviation in price_alerts(5): print(f\u0026#34;价格异常: {price:.2f}, 偏离: {deviation:.2f}\u0026#34;) 3. 组合多个迭代器 # def interleave(*iterables): \u0026#34;\u0026#34;\u0026#34;交替合并多个迭代器\u0026#34;\u0026#34;\u0026#34; iterators = [iter(it) for it in iterables] while iterators: for i, it in enumerate(iterators): try: yield next(it) except StopIteration: iterators.pop(i) break # 使用 list1 = [1, 2, 3] list2 = [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39;] list3 = [True, False] result = list(interleave(list1, list2, list3)) print(result) # [1, \u0026#39;a\u0026#39;, True, 2, \u0026#39;b\u0026#39;, False, 3, \u0026#39;c\u0026#39;] 性能优化技巧 # 何时使用列表推导式 # # ✅ 列表推导式：需要多次访问列表 numbers = [1, 2, 3, 4, 5] # 好：一次性创建列表 squares = [x ** 2 for x in numbers] for square in squares: # 多次遍历 print(square) 何时使用生成器 # # ✅ 生成器：数据量大，只遍历一次 numbers = range(1, 1000000) # 好：使用生成器节省内存 squares = (x ** 2 for x in numbers) for square in squares: # 只需遍历一次 print(square) 避免常见陷阱 # # 陷阱：循环变量泄漏 x = \u0026#39;original\u0026#39; result = [x for x in range(5)] # x 变成了 4！ print(x) # 4 # 正确：使用不同的变量名 y = \u0026#39;original\u0026#39; result = [x for x in range(5)] # y 保持不变 print(y) # \u0026#39;original\u0026#39; # 陷阱：重复使用生成器 gen = (x for x in range(5)) print(list(gen)) # [0, 1, 2, 3, 4] print(list(gen)) # [] - 生成器已耗尽 # 正确：需要时重新创建 def gen_func(): return (x for x in range(5)) print(list(gen_func())) # [0, 1, 2, 3, 4] print(list(gen_func())) # [0, 1, 2, 3, 4] Pythonic 代码风格 # 推荐的写法 # # ✅ 简洁的过滤和转换 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 获取偶数的平方 even_squares = [x**2 for x in numbers if x % 2 == 0] print(even_squares) # [4, 16, 36, 64, 100] # 字典推导式 students = {\u0026#39;Alice\u0026#39;: 90, \u0026#39;Bob\u0026#39;: 85, \u0026#39;Charlie\u0026#39;: 92} passed = {name: score for name, score in students.items() if score \u0026gt;= 90} print(passed) # {\u0026#39;Alice\u0026#39;: 90, \u0026#39;Charlie\u0026#39;: 92} # 生成器用于大数据 data = range(1000000) result = sum(x ** 2 for x in data) # 不创建中间列表 # 使用 enumerate 获取索引 words = [\u0026#39;hello\u0026#39;, \u0026#39;world\u0026#39;, \u0026#39;python\u0026#39;] indexed = [(i, word) for i, word in enumerate(words)] # 使用 zip 组合多个列表 names = [\u0026#39;Alice\u0026#39;, \u0026#39;Bob\u0026#39;, \u0026#39;Charlie\u0026#39;] ages = [25, 30, 35] combined = {name: age for name, age in zip(names, ages)} 不推荐的写法 # # ❌ 嵌套过深的推导式 matrix = [[1, 2], [3, 4], [5, 6]] result = [[cell for cell in row if cell % 2 == 0] for row in matrix if sum(row) \u0026gt; 4] # 太复杂，难以理解 # 好：拆分成多步 filtered_rows = [row for row in matrix if sum(row) \u0026gt; 4] result = [[cell for cell in row if cell % 2 == 0] for row in filtered_rows] 总结 # 类型 语法 特点 列表推导式 [x for x in items] 返回列表，占用内存 集合推导式 {x for x in items} 自动去重 字典推导式 {k: v for k, v in items} 创建字典 生成器表达式 (x for x in items) 惰性求值，节省内存 生成器函数 def gen(): yield x 可包含复杂逻辑 选择指南 # 列表推导式：需要多次遍历，数据量不大 生成器表达式：只遍历一次，数据量大 生成器函数：需要复杂逻辑或状态 字典/集合推导式：快速创建字典/集合 掌握这些技巧让你的 Python 代码更加简洁、高效和 Pythonic！\n📚 推荐阅读\nPEP 202 - List Comprehensions PEP 289 - Generator Expressions Python Wiki: List Comprehensions ","date":"2026-04-22","externalUrl":null,"permalink":"/posts/python/python-list-comprehensions-generators/","section":"文章","summary":"","title":"Python 列表推导式与生成器：高效编写代码的秘密","type":"posts"},{"content":"任何程序都需要日志系统来记录运行状态、调试问题和追踪错误。Python 内置的 logging 模块提供了完整的日志解决方案，远比 print() 语句更专业、更灵活。\n本文将带你全面掌握 Python logging 模块，从基础用法到高级配置。\n为什么不用 print()？ # 很多初学者习惯用 print() 来调试程序，这在小型脚本中是可以的。但对于正式项目，日志系统有不可替代的优势：\n特性 print() logging 日志级别 无 DEBUG/INFO/WARNING/ERROR/CRITICAL 输出位置 仅控制台 文件、控制台、网络等 格式控制 简单 完全可定制 性能影响 较大 可控制 多线程安全 一般 安全 可关闭调试信息 困难 容易 日志级别 # logging 模块定义了 5 个日志级别，从低到高：\nimport logging # 级别由低到高 logging.debug(\u0026#34;调试信息 - 开发时使用\u0026#34;) logging.info(\u0026#34;一般信息 - 程序运行状态\u0026#34;) logging.warning(\u0026#34;警告信息 - 潜在问题\u0026#34;) logging.error(\u0026#34;错误信息 - 程序出错\u0026#34;) logging.critical(\u0026#34;严重错误 - 严重问题\u0026#34;) 级别对应关系 # # 数值越小，级别越低 logging.DEBUG = 10 logging.INFO = 20 logging.WARNING = 30 logging.ERROR = 40 logging.CRITICAL = 50 设置日志级别 # 默认只显示 WARNING 及以上级别的日志：\nimport logging # 创建一个 logger logger = logging.getLogger(__name__) # 设置级别为 DEBUG（显示所有日志） logger.setLevel(logging.DEBUG) # 默认只会显示 WARNING 及以上的日志 # DEBUG 和 INFO 不会显示 基础配置 # 快速配置 # import logging # 最简单的方式 - 一行配置 logging.basicConfig(level=logging.DEBUG) # 现在所有级别都会显示 logging.debug(\u0026#34;这条会显示\u0026#34;) logging.info(\u0026#34;这条也会显示\u0026#34;) basicConfig 常用参数 # import logging logging.basicConfig( level=logging.DEBUG, # 设置级别 format=\u0026#39;%(asctime)s - %(name)s - %(levelname)s - %(message)s\u0026#39;, # 格式 datefmt=\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39;, # 日期格式 filename=\u0026#39;app.log\u0026#39;, # 输出到文件 filemode=\u0026#39;a\u0026#39;, # 文件模式：a=追加，w=覆盖 encoding=\u0026#39;utf-8\u0026#39; # 文件编码 ) logging.debug(\u0026#34;这条会写入文件\u0026#34;) Logger 对象 # 创建 Logger # import logging # 创建命名 logger logger = logging.getLogger(\u0026#39;my_app\u0026#39;) logger.setLevel(logging.DEBUG) # 也可以用 __name__ 自动获取模块名 logger = logging.getLogger(__name__) Handler（处理器） # Handler 决定日志输出到哪里：\nimport logging logger = logging.getLogger(\u0026#39;my_app\u0026#39;) logger.setLevel(logging.DEBUG) # 控制台处理器 console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) # 文件处理器 file_handler = logging.FileHandler(\u0026#39;app.log\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) file_handler.setLevel(logging.DEBUG) # 添加处理器到 logger logger.addHandler(console_handler) logger.addHandler(file_handler) # 同时输出到控制台和文件 logger.debug(\u0026#34;调试信息\u0026#34;) # 只在文件中 logger.info(\u0026#34;一般信息\u0026#34;) # 控制台和文件都有 Formatter（格式化器） # import logging # 创建 formatter formatter = logging.Formatter( \u0026#39;%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s\u0026#39;, datefmt=\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39; ) # 创建 handler 并设置 formatter console_handler = logging.StreamHandler() console_handler.setFormatter(formatter) # 添加到 logger logger = logging.getLogger(\u0026#39;my_app\u0026#39;) logger.addHandler(console_handler) logger.info(\u0026#34;格式化的日志信息\u0026#34;) Formatter 格式说明 # 格式 说明 示例 %(name)s Logger 名称 my_app %(levelname)s 日志级别 DEBUG/INFO %(message)s 日志消息 消息内容 %(asctime)s 时间 2024-01-01 10:30:00 %(filename)s 文件名 app.py %(funcName)s 函数名 main %(lineno)d 行号 42 %(process)d 进程 ID 1234 %(thread)d 线程 ID 5678 组合示例 # import logging def setup_logger(): \u0026#34;\u0026#34;\u0026#34;配置日志系统\u0026#34;\u0026#34;\u0026#34; # 创建 logger logger = logging.getLogger(\u0026#39;my_app\u0026#39;) logger.setLevel(logging.DEBUG) # 防止重复添加 handler if logger.handlers: return logger # 创建 formatter detailed_formatter = logging.Formatter( \u0026#39;%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s\u0026#39;, datefmt=\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39; ) simple_formatter = logging.Formatter( \u0026#39;%(levelname)s - %(message)s\u0026#39; ) # 控制台处理器 (只显示 INFO 及以上) console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_handler.setFormatter(simple_formatter) # 文件处理器 (记录所有级别) file_handler = logging.FileHandler(\u0026#39;app.log\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(detailed_formatter) # 添加处理器 logger.addHandler(console_handler) logger.addHandler(file_handler) return logger # 使用 logger = setup_logger() logger.debug(\u0026#34;调试信息 - 只在文件\u0026#34;) logger.info(\u0026#34;一般信息 - 控制台和文件\u0026#34;) logger.warning(\u0026#34;警告信息\u0026#34;) logger.error(\u0026#34;错误信息\u0026#34;) 日志轮转 # 日志文件会不断增长，使用 RotatingFileHandler 或 TimedRotatingFileHandler 可以自动管理日志文件。\n按大小轮转 # import logging from logging.handlers import RotatingFileHandler logger = logging.getLogger(\u0026#39;my_app\u0026#39;) logger.setLevel(logging.DEBUG) # 10MB 分割，保留 5 个备份 handler = RotatingFileHandler( \u0026#39;app.log\u0026#39;, maxBytes=10 * 1024 * 1024, # 10MB backupCount=5, encoding=\u0026#39;utf-8\u0026#39; ) handler.setLevel(logging.DEBUG) formatter = logging.Formatter(\u0026#39;%(asctime)s - %(levelname)s - %(message)s\u0026#39;) handler.setFormatter(formatter) logger.addHandler(handler) # 当文件达到 10MB 时，会自动轮转 # 保留 app.log, app.log.1, app.log.2 ... app.log.5 按时间轮转 # import logging from logging.handlers import TimedRotatingFileHandler logger = logging.getLogger(\u0026#39;my_app\u0026#39;) logger.setLevel(logging.DEBUG) # 每天午夜轮转，保留 7 天 handler = TimedRotatingFileHandler( \u0026#39;app.log\u0026#39;, when=\u0026#39;midnight\u0026#39;, # 何时轮转：midnight, H, D, W0-W6 interval=1, # 间隔 backupCount=7, # 保留 7 个备份 encoding=\u0026#39;utf-8\u0026#39; ) # 文件名后缀格式 handler.suffix = \u0026#39;%Y-%m-%d.log\u0026#39; formatter = logging.Formatter(\u0026#39;%(asctime)s - %(levelname)s - %(message)s\u0026#39;) handler.setFormatter(formatter) logger.addHandler(handler) # 生成的日志文件： # app.log.2024-01-01.log # app.log.2024-01-02.log # ... 实战：配置文件方式 # 使用字典配置 # import logging import logging.config # 日志配置字典 LOGGING_CONFIG = { \u0026#39;version\u0026#39;: 1, \u0026#39;disable_existing_loggers\u0026#39;: False, \u0026#39;formatters\u0026#39;: { \u0026#39;detailed\u0026#39;: { \u0026#39;format\u0026#39;: \u0026#39;%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s\u0026#39;, \u0026#39;datefmt\u0026#39;: \u0026#39;%Y-%m-%d %H:%M:%S\u0026#39; }, \u0026#39;simple\u0026#39;: { \u0026#39;format\u0026#39;: \u0026#39;%(levelname)s - %(message)s\u0026#39; } }, \u0026#39;handlers\u0026#39;: { \u0026#39;console\u0026#39;: { \u0026#39;class\u0026#39;: \u0026#39;logging.StreamHandler\u0026#39;, \u0026#39;level\u0026#39;: \u0026#39;INFO\u0026#39;, \u0026#39;formatter\u0026#39;: \u0026#39;simple\u0026#39;, \u0026#39;stream\u0026#39;: \u0026#39;ext://sys.stdout\u0026#39; }, \u0026#39;file\u0026#39;: { \u0026#39;class\u0026#39;: \u0026#39;logging.handlers.RotatingFileHandler\u0026#39;, \u0026#39;level\u0026#39;: \u0026#39;DEBUG\u0026#39;, \u0026#39;formatter\u0026#39;: \u0026#39;detailed\u0026#39;, \u0026#39;filename\u0026#39;: \u0026#39;app.log\u0026#39;, \u0026#39;maxBytes\u0026#39;: 10485760, # 10MB \u0026#39;backupCount\u0026#39;: 5, \u0026#39;encoding\u0026#39;: \u0026#39;utf-8\u0026#39; } }, \u0026#39;loggers\u0026#39;: { \u0026#39;my_app\u0026#39;: { \u0026#39;level\u0026#39;: \u0026#39;DEBUG\u0026#39;, \u0026#39;handlers\u0026#39;: [\u0026#39;console\u0026#39;, \u0026#39;file\u0026#39;], \u0026#39;propagate\u0026#39;: False } }, \u0026#39;root\u0026#39;: { \u0026#39;level\u0026#39;: \u0026#39;WARNING\u0026#39;, \u0026#39;handlers\u0026#39;: [\u0026#39;console\u0026#39;] } } # 应用配置 logging.config.dictConfig(LOGGING_CONFIG) # 使用 logger = logging.getLogger(\u0026#39;my_app\u0026#39;) logger.debug(\u0026#34;调试信息\u0026#34;) logger.info(\u0026#34;一般信息\u0026#34;) 使用 YAML 配置文件 # # logging.yaml version: 1 disable_existing_loggers: False formatters: default: format: \u0026#39;%(asctime)s - %(name)s - %(levelname)s - %(message)s\u0026#39; datefmt: \u0026#39;%Y-%m-%d %H:%M:%S\u0026#39; handlers: console: class: logging.StreamHandler level: INFO formatter: default stream: ext://sys.stdout file: class: logging.handlers.RotatingFileHandler level: DEBUG formatter: default filename: app.log maxBytes: 10485760 backupCount: 5 encoding: utf-8 root: level: DEBUG handlers: [console, file] import logging import logging.config import yaml # 加载 YAML 配置 with open(\u0026#39;logging.yaml\u0026#39;, \u0026#39;r\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as f: config = yaml.safe_load(f) logging.config.dictConfig(config) logger = logging.getLogger(__name__) logger.info(\u0026#34;配置完成\u0026#34;) 在类中使用日志 # 类的实例日志器 # import logging class MyClass: def __init__(self, value): self.logger = logging.getLogger(f\u0026#39;{__name__}.MyClass\u0026#39;) self.value = value self.logger.info(f\u0026#39;MyClass 实例化，value={value}\u0026#39;) def do_something(self): self.logger.debug(\u0026#34;执行 do_something\u0026#34;) try: result = self.value * 2 self.logger.info(f\u0026#34;计算结果: {result}\u0026#34;) return result except Exception as e: self.logger.error(f\u0026#34;发生错误: {e}\u0026#34;) raise # 使用 obj = MyClass(10) obj.do_something() 模块级日志器 # # mymodule.py import logging logger = logging.getLogger(__name__) class MyClass: def __init__(self, value): self.value = value logger.info(f\u0026#39;MyClass 初始化，value={value}\u0026#39;) def do_something(self): logger.debug(\u0026#34;执行操作\u0026#34;) return self.value * 2 # 外部使用时，可以统一配置 # import mymodule # mymodule.logger.setLevel(logging.DEBUG) 日志与异常 # 记录异常信息 # import logging logger = logging.getLogger(__name__) try: result = 10 / 0 except Exception as e: # 方式一：只记录异常消息 logger.error(f\u0026#34;发生错误: {e}\u0026#34;) # 方式二：记录异常信息和堆栈（推荐） logger.exception(\u0026#34;发生异常\u0026#34;) # 方式三：显式传递 exc_info=True logger.error(\u0026#34;发生错误\u0026#34;, exc_info=True) traceback 模块辅助 # import logging import traceback logger = logging.getLogger(__name__) try: # 可能出错的代码 pass except Exception: # 完整堆栈跟踪 logger.error(\u0026#34;异常详情:\\n\u0026#34; + traceback.format_exc()) 多模块日志 # 项目结构 # myproject/ ├── main.py ├── utils/ │ ├── __init__.py │ └── helper.py └── config.py main.py # import logging import utils.helper def setup_logging(): \u0026#34;\u0026#34;\u0026#34;统一配置所有模块的日志\u0026#34;\u0026#34;\u0026#34; logging.basicConfig( level=logging.DEBUG, format=\u0026#39;%(asctime)s - %(name)s - %(levelname)s - %(message)s\u0026#39;, datefmt=\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39; ) def main(): setup_logging() logger = logging.getLogger(\u0026#39;main\u0026#39;) logger.info(\u0026#34;程序启动\u0026#34;) utils.helper.process() logger.info(\u0026#34;程序结束\u0026#34;) if __name__ == \u0026#39;__main__\u0026#39;: main() utils/helper.py # import logging logger = logging.getLogger(\u0026#39;utils.helper\u0026#39;) def process(): logger.info(\u0026#34;开始处理\u0026#34;) # ... 处理逻辑 logger.info(\u0026#34;处理完成\u0026#34;) 输出示例：\n2024-01-01 10:00:00 - main - INFO - 程序启动 2024-01-01 10:00:00 - utils.helper - INFO - 开始处理 2024-01-01 10:00:00 - utils.helper - INFO - 处理完成 2024-01-01 10:00:00 - main - INFO - 程序结束 过滤日志 # 自定义过滤器 # import logging class ContextFilter(logging.Filter): \u0026#34;\u0026#34;\u0026#34;添加上下文信息的过滤器\u0026#34;\u0026#34;\u0026#34; def __init__(self): super().__init__() self.user_id = None self.request_id = None def filter(self, record): record.user_id = getattr(self, \u0026#39;user_id\u0026#39;, \u0026#39;anonymous\u0026#39;) record.request_id = getattr(self, \u0026#39;request_id\u0026#39;, \u0026#39;no-request\u0026#39;) return True # 使用 logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) filter_obj = ContextFilter() handler = logging.StreamHandler() handler.addFilter(filter_obj) logger.addHandler(handler) # 设置上下文 filter_obj.user_id = \u0026#39;user123\u0026#39; filter_obj.request_id = \u0026#39;req-456\u0026#39; logger.info(\u0026#34;带上下文的日志\u0026#34;) 根据级别过滤 # import logging class LevelFilter(logging.Filter): \u0026#34;\u0026#34;\u0026#34;只允许特定级别的日志通过\u0026#34;\u0026#34;\u0026#34; def __init__(self, level): super().__init__() self.level = level def filter(self, record): return record.levelno == self.level # 只记录 ERROR 级别 handler = logging.StreamHandler() handler.addFilter(LevelFilter(logging.ERROR)) logger = logging.getLogger(\u0026#39;my_app\u0026#39;) logger.addHandler(handler) 日志与 JSON # 结构化日志输出 # import logging import json from datetime import datetime class JSONFormatter(logging.Formatter): \u0026#34;\u0026#34;\u0026#34;JSON 格式的日志格式化器\u0026#34;\u0026#34;\u0026#34; def format(self, record): log_data = { \u0026#39;timestamp\u0026#39;: datetime.utcnow().isoformat(), \u0026#39;level\u0026#39;: record.levelname, \u0026#39;logger\u0026#39;: record.name, \u0026#39;message\u0026#39;: record.getMessage(), \u0026#39;module\u0026#39;: record.module, \u0026#39;function\u0026#39;: record.funcName, \u0026#39;line\u0026#39;: record.lineno } # 添加异常信息 if record.exc_info: log_data[\u0026#39;exception\u0026#39;] = self.formatException(record.exc_info) # 添加额外字段 if hasattr(record, \u0026#39;user_id\u0026#39;): log_data[\u0026#39;user_id\u0026#39;] = record.user_id return json.dumps(log_data) # 使用 handler = logging.StreamHandler() handler.setFormatter(JSONFormatter()) logger = logging.getLogger(\u0026#39;my_app\u0026#39;) logger.setLevel(logging.DEBUG) logger.addHandler(handler) # 添加额外字段 extra = {\u0026#39;user_id\u0026#39;: \u0026#39;user123\u0026#39;} logger.info(\u0026#34;用户登录\u0026#34;, extra=extra) 性能优化 # 避免字符串拼接 # import logging logger = logging.getLogger(__name__) # 低效 - 总是拼接字符串 logger.debug(\u0026#34;用户数据: \u0026#34; + str(user_data)) # 高效 - 只在需要时拼接 logger.debug(\u0026#34;用户数据: %s\u0026#34;, user_data) 使用 lazy formatting # # 方式一：% 格式化 logger.debug(\u0026#34;值: %s\u0026#34;, expensive_calculation()) # 方式二：Lazy 包装 from logging import StringFormatterMessage as _ # 不要这样 if logger.isEnabledFor(logging.DEBUG): logger.debug(f\u0026#34;复杂消息: {some_expensive_operation()}\u0026#34;) 常见问题 # 1. 日志重复输出 # # 问题：多次配置导致重复输出 logging.basicConfig(level=logging.DEBUG) # 第一次 logging.basicConfig(level=logging.DEBUG) # 第二次 # 解决：检查并移除现有 handler logger = logging.getLogger(__name__) if not logger.handlers: handler = logging.StreamHandler() logger.addHandler(handler) 2. 模块日志不生效 # # 问题：子模块日志没有正确配置 import mymodule mymodule.logger.setLevel(logging.DEBUG) # 需要手动设置 # 解决：使用父 logger 统一配置 logging.getLogger(\u0026#39;mymodule\u0026#39;).setLevel(logging.DEBUG) logging.getLogger(\u0026#39;mymodule.submodule\u0026#39;).setLevel(logging.DEBUG) 3. 中文日志乱码 # # 确保文件编码正确 handler = logging.FileHandler(\u0026#39;app.log\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) # 确保源文件保存为 UTF-8 # -*- coding: utf-8 -*- 最佳实践 # 使用命名 logger\nlogger = logging.getLogger(__name__) # 推荐 logger = logging.getLogger(\u0026#39;myapp\u0026#39;) # 也可以 不要使用 root logger\n# 不好 logging.info(\u0026#34;message\u0026#34;) # 好 logger = logging.getLogger(__name__) logger.info(\u0026#34;message\u0026#34;) 配置只执行一次\nif not logger.handlers: setup_logger() 合理的日志级别\nDEBUG: 详细调试信息 INFO: 程序运行状态 WARNING: 警告但不影响运行 ERROR: 错误但程序可继续 CRITICAL: 严重错误导致程序无法运行 不要记录敏感信息\n# 不好 logger.info(f\u0026#34;用户密码: {password}\u0026#34;) # 好 logger.info(\u0026#34;用户登录成功\u0026#34;) 总结 # Python 的 logging 模块提供了完整的日志处理能力：\n📊 多级别：DEBUG/INFO/WARNING/ERROR/CRITICAL 📍 多输出：控制台、文件、网络 🔄 轮转：按大小或时间自动切换文件 🎨 格式化：完全可定制的输出格式 🔧 过滤：按条件筛选日志 ⚡ 性能：支持延迟格式化和异步日志 掌握这些内容，你就能为 Python 项目建立完善的日志系统了！\n📚 推荐阅读\nPython logging 官方文档 Logging Cookbook 十二要素应用之日志 ","date":"2026-04-22","externalUrl":null,"permalink":"/posts/python/python-logging-guide/","section":"文章","summary":"","title":"Python 日志处理完全指南：logging 模块从入门到精通","type":"posts"},{"content":"程序运行中难免会遇到各种错误：文件不存在、网络连接失败、数据格式错误\u0026hellip;如何优雅地处理这些错误，而不是让程序直接崩溃？Python 的异常处理机制就是答案。\n本文将全面讲解 Python 的异常处理，从基础语法到高级技巧。\n为什么需要异常处理？ # 没有异常处理的问题 # # 没有异常处理的代码 def read_file(filename): with open(filename, \u0026#39;r\u0026#39;) as f: # 如果文件不存在，程序直接崩溃 return f.read() # 调用 content = read_file(\u0026#39;not_exists.txt\u0026#39;) # FileNotFoundError! print(\u0026#34;这段代码不会执行\u0026#34;) 有异常处理的好处 # # 有异常处理的代码 def read_file(filename): try: with open(filename, \u0026#39;r\u0026#39;) as f: return f.read() except FileNotFoundError: return \u0026#34;文件不存在\u0026#34; # 调用 content = read_file(\u0026#39;not_exists.txt\u0026#39;) # 优雅处理 print(f\u0026#34;结果: {content}\u0026#34;) # \u0026#34;文件不存在\u0026#34; 基础语法 # try-except 结构 # try: # 可能出错的代码 result = 10 / 0 except ZeroDivisionError: # 处理特定异常 print(\u0026#34;不能除以零!\u0026#34;) 基本结构 # try: # 可能抛出异常的代码 risky_code() except ExceptionType1: # 处理 ExceptionType1 handler1() except ExceptionType2: # 处理 ExceptionType2 handler2() else: # try 代码块执行成功时执行（可选） success_handler() finally: # 无论是否异常都执行（可选） cleanup() 捕获异常 # 捕获特定异常 # # 捕获单个异常 try: num = int(input(\u0026#34;请输入数字: \u0026#34;)) result = 10 / num except ValueError: print(\u0026#34;输入的不是有效数字\u0026#34;) except ZeroDivisionError: print(\u0026#34;不能除以零\u0026#34;) # 捕获多个异常（元组形式） try: with open(\u0026#39;file.txt\u0026#39;, \u0026#39;r\u0026#39;) as f: data = f.read() except (FileNotFoundError, PermissionError): print(\u0026#34;文件无法访问\u0026#34;) 捕获所有异常 # # 方式一：捕获 Exception try: result = risky_operation() except Exception as e: print(f\u0026#34;发生错误: {e}\u0026#34;) # 方式二：使用 except:（不推荐，不知道具体异常） try: result = risky_operation() except: print(\u0026#34;发生错误\u0026#34;) # 方式三：捕获 BaseException（包含 SystemExit 等） try: sys.exit() except BaseException as e: print(f\u0026#34;捕获到: {type(e).__name__}\u0026#34;) 异常对象 # try: x = 1 / 0 except ZeroDivisionError as e: # 异常对象属性 print(f\u0026#34;异常类型: {type(e).__name__}\u0026#34;) # ZeroDivisionError print(f\u0026#34;异常消息: {e}\u0026#34;) # division by zero print(f\u0026#34;异常描述: {str(e)}\u0026#34;) # division by zero 常见异常类型 # Python 内置异常 # # 常见异常类型及示例 # ValueError - 值错误 int(\u0026#34;abc\u0026#34;) # ValueError: invalid literal for int() # TypeError - 类型错误 \u0026#34;1\u0026#34; + 1 # TypeError: can only concatenate str # IndexError - 索引错误 lst = [1, 2, 3] lst[10] # IndexError: list index out of range # KeyError - 字典键错误 d = {\u0026#39;a\u0026#39;: 1} d[\u0026#39;b\u0026#39;] # KeyError: \u0026#39;b\u0026#39; # FileNotFoundError - 文件不存在 open(\u0026#39;not_exists.txt\u0026#39;) # AttributeError - 属性错误 [].append # 正常 [].nonexistent # AttributeError # ImportError - 导入错误 import nonexistent_module # StopIteration - 迭代器耗尽 it = iter([1, 2]) next(it); next(it); next(it) # StopIteration # AssertionError - 断言失败 assert 1 == 2 # KeyboardInterrupt - 用户中断 # Ctrl+C 触发 # SystemExit - 程序退出 import sys sys.exit() 异常层次结构 # BaseException ├── SystemExit ├── KeyboardInterrupt ├── GeneratorExit └── Exception ├── StopIteration ├── ArithmeticError │ ├── FloatingPointError │ ├── OverflowError │ └── ZeroDivisionError ├── LookupError │ ├── IndexError │ └── KeyError ├── OSError (IOError) │ ├── FileNotFoundError │ └── PermissionError ├── TypeError ├── ValueError └── ... raise 语句 # 抛出异常 # # 基本语法 raise ExceptionType(\u0026#34;错误消息\u0026#34;) # 示例 def validate_age(age): if age \u0026lt; 0: raise ValueError(\u0026#34;年龄不能为负数\u0026#34;) if age \u0026gt; 150: raise ValueError(\u0026#34;年龄不合理\u0026#34;) return age try: validate_age(-5) except ValueError as e: print(e) # 年龄不能为负数 重新抛出异常 # def read_config(): try: with open(\u0026#39;config.json\u0026#39;, \u0026#39;r\u0026#39;) as f: return json.load(f) except FileNotFoundError: # 重新包装异常 raise RuntimeError(\u0026#34;配置文件不存在，请检查路径\u0026#34;) from None except json.JSONDecodeError as e: # 保留原始异常链 raise RuntimeError(\u0026#34;配置文件格式错误\u0026#34;) from e try: read_config() except RuntimeError as e: print(f\u0026#34;错误: {e}\u0026#34;) if e.__cause__: print(f\u0026#34;原始原因: {e.__cause__}\u0026#34;) 异常链 # # 隐式异常链（异常在 except 块中发生时） try: int(\u0026#34;abc\u0026#34;) except ValueError as e: try: 1 / 0 except ZeroDivisionError: raise ValueError(\u0026#34;包装错误\u0026#34;) # __cause__ = None, __context__ = ValueError # 显式异常链 try: int(\u0026#34;abc\u0026#34;) except ValueError as e: raise RuntimeError(\u0026#34;新错误\u0026#34;) from e # __cause__ = ValueError else 子句 # else 在 try 代码块成功执行后执行：\ntry: with open(\u0026#39;data.txt\u0026#39;, \u0026#39;r\u0026#39;) as f: data = f.read() except FileNotFoundError: data = None print(\u0026#34;文件不存在，使用默认值\u0026#34;) else: print(\u0026#34;文件读取成功!\u0026#34;) # 只在成功时执行 print(f\u0026#34;文件长度: {len(data)}\u0026#34;) print(\u0026#34;程序继续执行\u0026#34;) finally 子句 # finally 无论是否异常都执行，常用于清理资源：\n基本用法 # try: file = open(\u0026#39;data.txt\u0026#39;, \u0026#39;w\u0026#39;) file.write(\u0026#34;Hello\u0026#34;) except Exception as e: print(f\u0026#34;错误: {e}\u0026#34;) finally: # 无论如何都会执行 print(\u0026#34;清理资源\u0026#34;) file.close() # 确保文件关闭 与 context manager 对比 # # 方式一：手动关闭 file = None try: file = open(\u0026#39;data.txt\u0026#39;, \u0026#39;r\u0026#39;) content = file.read() finally: if file: file.close() # 方式二：context manager（推荐） with open(\u0026#39;data.txt\u0026#39;, \u0026#39;r\u0026#39;) as file: content = file.read() # 自动关闭，无需 finally 资源清理模式 # 数据库连接 # import sqlite3 # 不推荐的写法 conn = sqlite3.connect(\u0026#39;test.db\u0026#39;) try: cursor = conn.cursor() cursor.execute(\u0026#39;SELECT * FROM users\u0026#39;) result = cursor.fetchall() finally: conn.close() # 确保关闭 # 推荐的写法（Python 3.7+） conn = sqlite3.connect(\u0026#39;test.db\u0026#39;) try: cursor = conn.cursor() cursor.execute(\u0026#39;SELECT * FROM users\u0026#39;) result = cursor.fetchall() except Exception as e: conn.rollback() raise else: conn.commit() finally: conn.close() 网络请求 # import requests def fetch_data(url): session = requests.Session() try: response = session.get(url, timeout=10) response.raise_for_status() return response.json() except requests.RequestException as e: raise RuntimeError(f\u0026#34;请求失败: {e}\u0026#34;) from e finally: session.close() 锁的释放 # import threading lock = threading.Lock() def thread_safe_operation(): lock.acquire() try: # 临界区代码 shared_resource += 1 finally: lock.release() # 确保锁被释放 # 或者使用 context manager from contextlib import contextmanager @contextmanager def acquired(lock): lock.acquire() try: yield finally: lock.release() def thread_safe_operation(): with acquired(lock): shared_resource += 1 自定义异常 # 创建自定义异常 # # 基础自定义异常 class ValidationError(Exception): \u0026#34;\u0026#34;\u0026#34;验证错误异常\u0026#34;\u0026#34;\u0026#34; pass def validate_email(email): if \u0026#39;@\u0026#39; not in email: raise ValidationError(f\u0026#34;无效的邮箱地址: {email}\u0026#34;) try: validate_email(\u0026#34;invalid_email\u0026#34;) except ValidationError as e: print(f\u0026#34;验证失败: {e}\u0026#34;) # 带详细信息的异常 class APIError(Exception): \u0026#34;\u0026#34;\u0026#34;API 错误\u0026#34;\u0026#34;\u0026#34; def __init__(self, message, status_code=None, response=None): super().__init__(message) self.status_code = status_code self.response = response # 使用自定义异常 import requests def get_user(user_id): response = requests.get(f\u0026#39;https://api.example.com/users/{user_id}\u0026#39;) if response.status_code == 404: raise APIError( f\u0026#34;用户 {user_id} 不存在\u0026#34;, status_code=404, response=response.text ) return response.json() 异常类最佳实践 # # 推荐：继承自具体异常而不是 Exception class NetworkError(ConnectionError): \u0026#34;\u0026#34;\u0026#34;网络连接错误\u0026#34;\u0026#34;\u0026#34; pass class TimeoutError(ConnectionError): \u0026#34;\u0026#34;\u0026#34;请求超时错误\u0026#34;\u0026#34;\u0026#34; pass # 添加额外属性 class ConfigurationError(Exception): \u0026#34;\u0026#34;\u0026#34;配置错误\u0026#34;\u0026#34;\u0026#34; def __init__(self, message, config_key=None, config_value=None): super().__init__(message) self.config_key = config_key self.config_value = config_value def __str__(self): msg = super().__str__() if self.config_key: msg += f\u0026#34; (key={self.config_key}, value={self.config_value})\u0026#34; return msg try: raise ConfigurationError(\u0026#34;配置值无效\u0026#34;, \u0026#34;database_url\u0026#34;, \u0026#34;invalid\u0026#34;) except ConfigurationError as e: print(f\u0026#34;{e} - 键: {e.config_key}, 值: {e.config_value}\u0026#34;) 异常处理最佳实践 # 1. 精准捕获异常 # # ❌ 过于宽泛 try: do_something() except Exception: pass # ✅ 精准捕获 try: do_something() except ValueError: handle_value_error() except FileNotFoundError: handle_file_not_found() except Exception: handle_unexpected_error() # 最后捕获所有未处理的异常 2. 异常与业务逻辑分离 # # ❌ 异常处理与业务逻辑混杂 def process_order(order): try: validate_order(order) charge_customer(order) ship_order(order) send_confirmation(order) except ValidationError: print(\u0026#34;订单验证失败\u0026#34;) except PaymentError: print(\u0026#34;支付失败\u0026#34;) except ShippingError: print(\u0026#34;发货失败\u0026#34;) # ✅ 分离异常处理 def process_order(order): # 业务逻辑 - 只处理正常流程 validate_order(order) charge_customer(order) ship_order(order) send_confirmation(order) # 异常处理在调用方 try: process_order(order) except ValidationError: print(\u0026#34;订单验证失败\u0026#34;) except PaymentError: print(\u0026#34;支付失败\u0026#34;) except ShippingError: print(\u0026#34;发货失败\u0026#34;) 3. 记录异常而非静默处理 # import logging logger = logging.getLogger(__name__) try: risky_operation() except Exception as e: # ❌ 静默处理 pass # ✅ 记录异常 logger.exception(\u0026#34;操作失败\u0026#34;) # 自动包含 traceback # 或 logger.error(f\u0026#34;操作失败: {e}\u0026#34;, exc_info=True) 4. 不要过度使用异常 # # ❌ 滥用异常处理控制流程 try: if items[10]: pass except IndexError: print(\u0026#34;索引超出范围\u0026#34;) # ✅ 使用条件判断 if len(items) \u0026gt; 10 and items[10]: pass # ❌ 过度使用 try-except try: value = int(user_input) except ValueError: value = 0 # ✅ 使用默认值 value = int(user_input) if user_input.isdigit() else 0 # 或者 try: value = int(user_input) except ValueError: value = int(user_input) # 尝试默认值 异常处理与性能 # import timeit # try-except 带来的开销 def with_exception(): try: return 1 except: return 0 def without_exception(): return 1 # 测试 print(timeit.timeit(with_exception, number=1000000)) print(timeit.timeit(without_exception, number=1000000)) # 经验法则： # - 异常控制流：开销明显 # - 异常捕获（不发生异常时）：开销很小 # - 只在异常情况使用，不要用于正常流程控制 常见场景处理 # 1. 文件操作 # # 读取文件的多种方式 # 方式一：try-except-finally file = None try: file = open(\u0026#39;data.txt\u0026#39;, \u0026#39;r\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) content = file.read() except FileNotFoundError: print(\u0026#34;文件不存在\u0026#34;) content = None except UnicodeDecodeError: print(\u0026#34;编码错误\u0026#34;) content = None finally: if file: file.close() # 方式二：context manager（推荐） try: with open(\u0026#39;data.txt\u0026#39;, \u0026#39;r\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as file: content = file.read() except FileNotFoundError: content = None # 文件自动关闭 2. JSON 解析 # import json # 安全解析 JSON def safe_json_loads(json_string, default=None): try: return json.loads(json_string) except json.JSONDecodeError as e: print(f\u0026#34;JSON 解析错误: {e}\u0026#34;) return default # 测试 print(safe_json_loads(\u0026#39;{\u0026#34;valid\u0026#34;: true}\u0026#39;)) # {\u0026#39;valid\u0026#39;: True} print(safe_json_loads(\u0026#39;invalid json\u0026#39;)) # None print(safe_json_loads(\u0026#39;invalid\u0026#39;, default={})) # {} 3. 字典安全访问 # # 安全获取字典值 def safe_get(dictionary, *keys, default=None): \u0026#34;\u0026#34;\u0026#34;安全获取嵌套字典的值\u0026#34;\u0026#34;\u0026#34; result = dictionary for key in keys: try: result = result[key] except (KeyError, TypeError, IndexError): return default return result # 使用 data = {\u0026#39;user\u0026#39;: {\u0026#39;profile\u0026#39;: {\u0026#39;name\u0026#39;: \u0026#39;Alice\u0026#39;}}} print(safe_get(data, \u0026#39;user\u0026#39;, \u0026#39;profile\u0026#39;, \u0026#39;name\u0026#39;)) # Alice print(safe_get(data, \u0026#39;user\u0026#39;, \u0026#39;address\u0026#39;, \u0026#39;city\u0026#39;)) # None print(safe_get(data, \u0026#39;user\u0026#39;, \u0026#39;address\u0026#39;, \u0026#39;city\u0026#39;, default=\u0026#39;未知\u0026#39;)) # 未知 4. 类型转换安全 # def safe_int(value, default=0): \u0026#34;\u0026#34;\u0026#34;安全转换为整数\u0026#34;\u0026#34;\u0026#34; try: return int(value) except (ValueError, TypeError): return default def safe_float(value, default=0.0): \u0026#34;\u0026#34;\u0026#34;安全转换为浮点数\u0026#34;\u0026#34;\u0026#34; try: return float(value) except (ValueError, TypeError): return default def safe_bool(value): \u0026#34;\u0026#34;\u0026#34;安全转换为布尔值\u0026#34;\u0026#34;\u0026#34; if isinstance(value, bool): return value try: return bool(value) except (ValueError, TypeError): return False # 测试 print(safe_int(\u0026#34;123\u0026#34;)) # 123 print(safe_int(\u0026#34;abc\u0026#34;)) # 0 print(safe_int(\u0026#34;42.5\u0026#34;)) # 42 print(safe_float(\u0026#34;3.14\u0026#34;)) # 3.14 print(safe_float(\u0026#34;abc\u0026#34;)) # 0.0 5. 网络请求 # import requests from requests.exceptions import ( ConnectionError, Timeout, HTTPError, RequestException ) def robust_request(url, method=\u0026#39;GET\u0026#39;, max_retries=3): \u0026#34;\u0026#34;\u0026#34;带重试的健壮请求\u0026#34;\u0026#34;\u0026#34; for attempt in range(max_retries): try: if method == \u0026#39;GET\u0026#39;: response = requests.get(url, timeout=10) elif method == \u0026#39;POST\u0026#39;: response = requests.post(url, timeout=10) else: raise ValueError(f\u0026#34;不支持的 HTTP 方法: {method}\u0026#34;) response.raise_for_status() return response.json() except ConnectionError: if attempt \u0026lt; max_retries - 1: print(f\u0026#34;连接失败，重试 {attempt + 1}/{max_retries}\u0026#34;) continue raise RuntimeError(\u0026#34;无法连接到服务器\u0026#34;) except Timeout: if attempt \u0026lt; max_retries - 1: print(f\u0026#34;请求超时，重试 {attempt + 1}/{max_retries}\u0026#34;) continue raise RuntimeError(\u0026#34;请求超时\u0026#34;) except HTTPError as e: if response.status_code == 404: raise RuntimeError(f\u0026#34;资源不存在: {url}\u0026#34;) elif response.status_code \u0026gt;= 500: if attempt \u0026lt; max_retries - 1: print(f\u0026#34;服务器错误，重试\u0026#34;) continue raise RuntimeError(f\u0026#34;HTTP 错误: {e}\u0026#34;) except RequestException as e: raise RuntimeError(f\u0026#34;请求失败: {e}\u0026#34;) 异常处理的测试 # import pytest def divide(a, b): if b == 0: raise ValueError(\u0026#34;除数不能为零\u0026#34;) return a / b # 测试正常情况 def test_divide_success(): assert divide(10, 2) == 5 # 测试异常情况 def test_divide_by_zero(): with pytest.raises(ValueError) as exc_info: divide(10, 0) assert \u0026#34;除数不能为零\u0026#34; in str(exc_info.value) # 测试异常属性 def test_custom_exception_attributes(): class APIError(Exception): def __init__(self, message, code): super().__init__(message) self.code = code with pytest.raises(APIError) as exc_info: raise APIError(\u0026#34;Not Found\u0026#34;, 404) assert exc_info.value.code == 404 调试技巧 # 使用 traceback 模块 # import traceback try: 1 / 0 except Exception: # 打印完整堆栈 traceback.print_exc() # 获取堆栈字符串 stack_trace = traceback.format_exc() print(f\u0026#34;堆栈信息: {stack_trace}\u0026#34;) 使用 logging 模块 # import logging logger = logging.getLogger(__name__) try: do_something() except Exception: logger.exception(\u0026#34;操作失败\u0026#34;) # 自动记录完整 traceback 使用 IPython 调试 # # 在异常发生后进入调试器 try: do_something() except Exception: import pdb pdb.post_mortem() # 或使用 ipdb # pip install ipdb try: do_something() except Exception: import ipdb ipdb.post_mortem() 总结 # 异常处理关键字 # 关键字 作用 try 包裹可能出错的代码 except 捕获并处理异常 else try 成功执行后运行 finally 无论是否异常都执行 raise 抛出异常 最佳实践 # 精准捕获：只捕获能处理的异常 记录日志：不要静默处理异常 资源清理：使用 finally 或 context manager 异常链：保留原始异常信息 分层处理：在合适的位置处理异常 不要滥用：异常用于异常情况，不是流程控制 异常处理流程 # try: # 可能抛出异常的代码 risky_code() except SpecificError: # 处理特定错误 handle_specific() except AnotherError: # 处理另一种错误 handle_another() else: # try 成功完成时执行 success_handler() finally: # 清理资源 cleanup() 掌握异常处理，让你的 Python 程序更加健壮！\n📚 推荐阅读\nPython 官方文档：错误与异常 异常处理最佳实践 Real Python: Exception Handling ","date":"2026-04-22","externalUrl":null,"permalink":"/posts/python/python-exception-handling/","section":"文章","summary":"","title":"Python 异常处理完全指南：try/except/finally 实战","type":"posts"},{"content":"装饰器是 Python 中最强大也最容易被误解的特性之一。它允许你在不修改原函数代码的情况下，给函数添加新功能。装饰器在框架、库和大型项目中无处不在：日志记录、性能测试、权限验证、缓存等都可以用它来实现。\n本文将带你从零开始，彻底理解装饰器的原理和使用方法。\n什么是装饰器？ # 装饰器的概念 # 装饰器本质上是一个函数，它接受一个函数作为参数，并返回一个新的函数。想象一下给函数\u0026quot;穿上一件外衣\u0026quot;——这就是装饰器的作用。\n简单类比 # # 装饰器就像给函数添加包装 def my_function(): return \u0026#34;Hello\u0026#34; # 装饰前 result = my_function() # \u0026#34;Hello\u0026#34; # 装饰后（添加了日志） result = logged(my_function)() # \u0026#34;Hello\u0026#34; + 日志 基本语法 # # 使用 @decorator_name 语法 @my_decorator def my_function(): return \u0026#34;Hello\u0026#34; # 等价于 def my_function(): return \u0026#34;Hello\u0026#34; my_function = my_decorator(my_function) 第一个装饰器 # 最简单的装饰器 # # 定义装饰器 def my_decorator(func): \u0026#34;\u0026#34;\u0026#34;一个简单的装饰器\u0026#34;\u0026#34;\u0026#34; def wrapper(): print(\u0026#34;函数执行前\u0026#34;) result = func() print(\u0026#34;函数执行后\u0026#34;) return result return wrapper # 使用装饰器 @my_decorator def say_hello(): print(\u0026#34;Hello!\u0026#34;) # 调用 say_hello() # 输出: # 函数执行前 # Hello! # 函数执行后 装饰器的工作原理 # # 逐步分解装饰器的执行过程 # 1. 定义原始函数 def say_hello(): print(\u0026#34;Hello!\u0026#34;) # 2. 将函数传给装饰器 decorated = my_decorator(say_hello) # 3. decorated 是 wrapper 函数 print(type(decorated)) # \u0026lt;class \u0026#39;function\u0026#39;\u0026gt; # 4. 调用 decorated decorated() # 5. @my_decorator 语法就是上面步骤的简写 @my_decorator def say_hello(): print(\u0026#34;Hello!\u0026#34;) # 等价于 def say_hello(): print(\u0026#34;Hello!\u0026#34;) say_hello = my_decorator(say_hello) 带参数的装饰器 # 处理函数参数 # def trace_calls(func): \u0026#34;\u0026#34;\u0026#34;追踪函数调用的装饰器\u0026#34;\u0026#34;\u0026#34; def wrapper(*args, **kwargs): print(f\u0026#34;调用 {func.__name__}，参数: args={args}, kwargs={kwargs}\u0026#34;) result = func(*args, **kwargs) print(f\u0026#34;{func.__name__} 返回: {result}\u0026#34;) return result return wrapper @trace_calls def add(a, b): return a + b @trace_calls def greet(name, greeting=\u0026#34;Hello\u0026#34;): return f\u0026#34;{greeting}, {name}!\u0026#34; # 测试 add(3, 5) # 输出: # 调用 add，参数: args=(3, 5), kwargs={} # add 返回: 8 greet(\u0026#34;Alice\u0026#34;) # 输出: # 调用 greet，参数: args=(\u0026#39;Alice\u0026#39;,), kwargs={} # greet 返回: Hello, Alice! greet(\u0026#34;Bob\u0026#34;, greeting=\u0026#34;Hi\u0026#34;) # 输出: # 调用 greet，参数: args=(\u0026#39;Bob\u0026#39;,), kwargs={\u0026#39;greeting\u0026#39;: \u0026#39;Hi\u0026#39;} # greet 返回: Hi, Bob! *args 和 **kwargs # def debug(func): \u0026#34;\u0026#34;\u0026#34;打印函数所有参数的装饰器\u0026#34;\u0026#34;\u0026#34; def wrapper(*args, **kwargs): # 位置参数 print(f\u0026#34;位置参数: {args}\u0026#34;) # 关键字参数 print(f\u0026#34;关键字参数: {kwargs}\u0026#34;) # 调用原函数 result = func(*args, **kwargs) return result return wrapper @debug def process(*args, **kwargs): print(f\u0026#34;处理数据: {args}, {kwargs}\u0026#34;) process(1, 2, 3, name=\u0026#34;test\u0026#34;, value=42) # 输出: # 位置参数: (1, 2, 3) # 关键字参数: {\u0026#39;name\u0026#39;: \u0026#39;test\u0026#39;, \u0026#39;value\u0026#39;: 42} # 处理数据: (1, 2, 3), {\u0026#39;name\u0026#39;: \u0026#39;test\u0026#39;, \u0026#39;value\u0026#39;: 42} 保留原函数信息 # 问题：函数元数据丢失 # def simple_decorator(func): def wrapper(*args, **kwargs): print(\u0026#34;调用前\u0026#34;) return func(*args, **kwargs) return wrapper @simple_decorator def hello(): \u0026#34;\u0026#34;\u0026#34;这是 hello 函数\u0026#34;\u0026#34;\u0026#34; return \u0026#34;Hello\u0026#34; print(hello.__name__) # wrapper - 函数名变成了 wrapper！ print(hello.__doc__) # None - 文档字符串也丢失了！ 解决方案：使用 functools.wraps # import functools def simple_decorator(func): @functools.wraps(func) # 保留原函数元数据 def wrapper(*args, **kwargs): print(\u0026#34;调用前\u0026#34;) return func(*args, **kwargs) return wrapper @simple_decorator def hello(): \u0026#34;\u0026#34;\u0026#34;这是 hello 函数\u0026#34;\u0026#34;\u0026#34; return \u0026#34;Hello\u0026#34; print(hello.__name__) # hello - 保留了原函数名！ print(hello.__doc__) # 这是 hello 函数 - 保留了文档字符串！ functools.wraps 的作用 # import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper # functools.wraps 会复制以下属性： # - __name__ 函数名 # - __doc__ 文档字符串 # - __qualname__ 限定名称 # - __module__ 模块名 # - __annotations__ 参数注解 # - __dict__ 其他属性 # 手动复制（等价于 @functools.wraps） def manual_wraps(wrapper, func): wrapper.__name__ = func.__name__ wrapper.__doc__ = func.__doc__ # ... 其他属性 return wrapper 带参数的装饰器 # 装饰器工厂函数 # # 装饰器工厂：返回装饰器的函数 def repeat(times): \u0026#34;\u0026#34;\u0026#34;重复执行函数的装饰器工厂\u0026#34;\u0026#34;\u0026#34; def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): results = [] for _ in range(times): result = func(*args, **kwargs) results.append(result) return results return wrapper return decorator # 使用 @repeat(times=3) def say_hello(): return \u0026#34;Hello!\u0026#34; print(say_hello()) # [\u0026#39;Hello!\u0026#39;, \u0026#39;Hello!\u0026#39;, \u0026#39;Hello!\u0026#39;] @repeat(times=2) def add(a, b): return a + b print(add(3, 5)) # [8, 8] 多层参数的装饰器 # def log(level): \u0026#34;\u0026#34;\u0026#34;带日志级别的装饰器工厂\u0026#34;\u0026#34;\u0026#34; def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f\u0026#34;[{level}] 调用 {func.__name__}\u0026#34;) return func(*args, **kwargs) return wrapper return decorator # 使用 @log(\u0026#34;DEBUG\u0026#34;) def debug_function(): pass @log(\u0026#34;INFO\u0026#34;) def info_function(): pass @log(\u0026#34;WARNING\u0026#34;) def warning_function(): pass debug_function() # [DEBUG] 调用 debug_function info_function() # [INFO] 调用 info_function warning_function() # [WARNING] 调用 warning_function 类装饰器 # 函数装饰器 vs 类装饰器 # # 函数装饰器 def func_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper # 类装饰器 class ClassDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): return self.func(*args, **kwargs) @ClassDecorator def decorated_function(): return \u0026#34;Hello\u0026#34; 类作为装饰器 # import functools import time class Timer: \u0026#34;\u0026#34;\u0026#34;计时器装饰器类\u0026#34;\u0026#34;\u0026#34; def __init__(self, func): functools.update_wrapper(self, func) self.func = func def __call__(self, *args, **kwargs): start = time.time() result = self.func(*args, **kwargs) end = time.time() print(f\u0026#34;{self.func.__name__} 执行耗时: {end - start:.4f}秒\u0026#34;) return result @Timer def slow_function(): time.sleep(1) return \u0026#34;完成\u0026#34; @Timer def fast_function(): return \u0026#34;完成\u0026#34; slow_function() # slow_function 执行耗时: 1.0001秒 fast_function() # fast_function 执行耗时: 0.0000秒 类装饰器带参数 # class Repeat: \u0026#34;\u0026#34;\u0026#34;带参数的类装饰器\u0026#34;\u0026#34;\u0026#34; def __init__(self, times): self.times = times def __call__(self, func): @functools.wraps(func) def wrapper(*args, **kwargs): results = [] for _ in range(self.times): results.append(func(*args, **kwargs)) return results return wrapper @Repeat(times=3) def get_value(): return 42 print(get_value()) # [42, 42, 42] 装饰器叠加 # 多个装饰器 # def decorator1(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(\u0026#34;装饰器 1 - 开始\u0026#34;) result = func(*args, **kwargs) print(\u0026#34;装饰器 1 - 结束\u0026#34;) return result return wrapper def decorator2(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(\u0026#34;装饰器 2 - 开始\u0026#34;) result = func(*args, **kwargs) print(\u0026#34;装饰器 2 - 结束\u0026#34;) return result return wrapper # 装饰器从上到下应用 @decorator1 @decorator2 def my_function(): print(\u0026#34;执行 my_function\u0026#34;) my_function() # 执行顺序： # 装饰器 1 - 开始 # 装饰器 2 - 开始 # 执行 my_function # 装饰器 2 - 结束 # 装饰器 1 - 结束 装饰器顺序的影响 # # 顺序不同，结果不同 @decorator_a @decorator_b def process(): pass # 执行顺序：decorator_a → decorator_b → process → decorator_b → decorator_a # 相当于 process = decorator_a(decorator_b(process)) # 示例 def uppercase(func): @functools.wraps(func) def wrapper(): return func().upper() return wrapper def add_exclamation(func): @functools.wraps(func) def wrapper(): return func() + \u0026#34;!\u0026#34; return wrapper @add_exclamation @uppercase def greet(): return \u0026#34;hello\u0026#34; print(greet()) # HELLO! @uppercase @add_exclamation def greet2(): return \u0026#34;hello\u0026#34; print(greet2()) # hello! 实战装饰器 # 1. 日志装饰器 # import functools import logging def log(level=\u0026#34;INFO\u0026#34;): \u0026#34;\u0026#34;\u0026#34;日志装饰器\u0026#34;\u0026#34;\u0026#34; def decorator(func): logger = logging.getLogger(func.__module__) @functools.wraps(func) def wrapper(*args, **kwargs): logger.log( getattr(logging, level), f\u0026#34;调用 {func.__name__}({args}, {kwargs})\u0026#34; ) try: result = func(*args, **kwargs) logger.log( getattr(logging, level), f\u0026#34;{func.__name__} 返回 {result}\u0026#34; ) return result except Exception as e: logger.error(f\u0026#34;{func.__name__} 抛出异常: {e}\u0026#34;) raise return wrapper return decorator # 使用 @log(\u0026#34;DEBUG\u0026#34;) def complex_function(x, y): return x / y @log(\u0026#34;ERROR\u0026#34;) def another_function(): pass 2. 计时装饰器 # import functools import time def timer(func): \u0026#34;\u0026#34;\u0026#34;函数执行时间计时器\u0026#34;\u0026#34;\u0026#34; @functools.wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) end = time.perf_counter() print(f\u0026#34;{func.__name__} 执行耗时: {end - start:.4f}秒\u0026#34;) return result return wrapper def async_timer(func): \u0026#34;\u0026#34;\u0026#34;异步函数计时器\u0026#34;\u0026#34;\u0026#34; @functools.wraps(func) async def wrapper(*args, **kwargs): start = time.perf_counter() result = await func(*args, **kwargs) end = time.perf_counter() print(f\u0026#34;{func.__name__} 执行耗时: {end - start:.4f}秒\u0026#34;) return result return wrapper @timer def sort_list(n): return sorted([i for i in range(n, 0, -1)]) sort_list(10000) # sort_list 执行耗时: 0.0023秒 3. 缓存装饰器（记忆化） # import functools def memoize(func): \u0026#34;\u0026#34;\u0026#34;缓存函数结果的装饰器\u0026#34;\u0026#34;\u0026#34; cache = {} @functools.wraps(func) def wrapper(*args, **kwargs): # 创建可哈希的键 key = str(args) + str(kwargs) if key not in cache: print(f\u0026#34;计算 {func.__name__}({args}, {kwargs})\u0026#34;) cache[key] = func(*args, **kwargs) else: print(f\u0026#34;从缓存读取 {func.__name__}({args}, {kwargs})\u0026#34;) return cache[key] return wrapper @memoize def fibonacci(n): if n \u0026lt; 2: return n return fibonacci(n - 1) + fibonacci(n - 2) print(fibonacci(10)) # 使用缓存后，计算快很多 # 第一次计算并缓存每个值 # 后续从缓存读取 4. 参数验证装饰器 # import functools def validate_args(**validators): \u0026#34;\u0026#34;\u0026#34;验证函数参数的装饰器\u0026#34;\u0026#34;\u0026#34; def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): # 获取函数参数名 import inspect sig = inspect.signature(func) bound = sig.bind(*args, **kwargs) bound.apply_defaults() # 验证每个参数 for param_name, validator in validators.items(): value = bound.arguments.get(param_name) if not validator(value): raise ValueError(f\u0026#34;参数 {param_name} 验证失败: {value}\u0026#34;) return func(*args, **kwargs) return wrapper return decorator @validate_args(age=lambda x: x \u0026gt;= 0, name=lambda x: len(x) \u0026gt; 0) def create_user(name, age): return {\u0026#34;name\u0026#34;: name, \u0026#34;age\u0026#34;: age} create_user(\u0026#34;Alice\u0026#34;, 25) # 正常 # create_user(\u0026#34;\u0026#34;, -5) # ValueError: 参数 age 验证失败 5. 重试装饰器 # import functools import time def retry(max_attempts=3, delay=1, exceptions=(Exception,)): \u0026#34;\u0026#34;\u0026#34;重试装饰器\u0026#34;\u0026#34;\u0026#34; def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): attempts = 0 while attempts \u0026lt; max_attempts: try: return func(*args, **kwargs) except exceptions as e: attempts += 1 if attempts \u0026gt;= max_attempts: raise print(f\u0026#34;尝试 {attempts} 失败，重试中... ({e})\u0026#34;) time.sleep(delay) return wrapper return decorator @retry(max_attempts=3, delay=1, exceptions=(ConnectionError,)) def fetch_data(): # 模拟不稳定操作 import random if random.random() \u0026lt; 0.7: raise ConnectionError(\u0026#34;网络不稳定\u0026#34;) return \u0026#34;数据获取成功\u0026#34; fetch_data() # 可能会重试几次后成功 6. 权限验证装饰器 # import functools class User: def __init__(self, name, role): self.name = name self.role = role def require_permission(permission): \u0026#34;\u0026#34;\u0026#34;权限验证装饰器\u0026#34;\u0026#34;\u0026#34; def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): # 从参数或全局获取当前用户 user = kwargs.get(\u0026#39;user\u0026#39;) or (args[0] if args else None) if not user or not hasattr(user, \u0026#39;role\u0026#39;): raise PermissionError(\u0026#34;需要登录\u0026#34;) if user.role != permission and user.role != \u0026#39;admin\u0026#39;: raise PermissionError(f\u0026#34;需要 {permission} 权限\u0026#34;) return func(*args, **kwargs) return wrapper return decorator class Document: def __init__(self, title): self.title = title @require_permission(\u0026#39;editor\u0026#39;) def edit(self, user, content): print(f\u0026#34;{user.name} 编辑了文档: {content}\u0026#34;) @require_permission(\u0026#39;viewer\u0026#39;) def view(self, user): print(f\u0026#34;{user.name}查看了文档: {self.title}\u0026#34;) admin = User(\u0026#34;Admin\u0026#34;, \u0026#34;admin\u0026#34;) editor = User(\u0026#34;Alice\u0026#34;, \u0026#34;editor\u0026#34;) viewer = User(\u0026#34;Bob\u0026#34;, \u0026#34;viewer\u0026#34;) doc = Document(\u0026#34;报告\u0026#34;) doc.edit(user=editor, content=\u0026#34;新内容\u0026#34;) # 可以 doc.edit(user=viewer, content=\u0026#34;新内容\u0026#34;) # PermissionError doc.view(user=viewer) # 可以 doc.view(user=editor) # 可以（admin 权限） functools 模块详解 # wraps 函数 # import functools # functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES) # 默认复制的属性 WRAPPER_ASSIGNMENTS = ( \u0026#39;__module__\u0026#39;, \u0026#39;__name__\u0026#39;, \u0026#39;__qualname__\u0026#39;, \u0026#39;__annotations__\u0026#39;, \u0026#39;__doc__\u0026#39; ) # 默认更新的属性 WRAPPER_UPDATES = (\u0026#39;__dict__\u0026#39;,) # 示例：自定义复制的属性 def my_decorator(func): @functools.wraps(func, assigned=(\u0026#39;__name__\u0026#39;, \u0026#39;__doc__\u0026#39;)) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper update_wrapper # # wraps 实际上是 update_wrapper 的装饰器版本 def my_decorator(func): def wrapper(*args, **kwargs): return func(*args, **kwargs) return functools.update_wrapper(wrapper, func) lru_cache # import functools # LRU (Least Recently Used) 缓存 @functools.lru_cache(maxsize=128) def expensive_computation(n): print(f\u0026#34;计算 {n}...\u0026#34;) return n * n expensive_computation(10) # 计算 10... expensive_computation(10) # 使用缓存，不再打印 # 使用参数 @functools.lru_cache(maxsize=None) # 无限制缓存 def fibonacci(n): if n \u0026lt; 2: return n return fibonacci(n - 1) + fibonacci(n - 2) # 查看缓存状态 print(fibonacci.cache_info()) # CacheInfo(hits=5, misses=11, maxsize=128, currsize=11) 装饰器在框架中的应用 # Flask 风格 # # Flask 中的装饰器用法示例 from functools import wraps app = {} def route(path): \u0026#34;\u0026#34;\u0026#34;Flask 风格路由装饰器\u0026#34;\u0026#34;\u0026#34; def decorator(func): app[path] = func @functools.wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper return decorator @route(\u0026#39;/\u0026#39;) def index(): return \u0026#39;首页\u0026#39; @route(\u0026#39;/about\u0026#39;) def about(): return \u0026#39;关于\u0026#39; print(app) # {\u0026#39;/\u0026#39;: index, \u0026#39;/about\u0026#39;: about} Django 风格 # # Django 的 method_decorator 示例 import functools def login_required(func): \u0026#34;\u0026#34;\u0026#34;登录验证装饰器\u0026#34;\u0026#34;\u0026#34; @functools.wraps(func) def wrapper(self, request, *args, **kwargs): if not request.user.is_authenticated: return redirect(\u0026#39;/login\u0026#39;) return func(self, request, *args, **kwargs) return wrapper # 使用在类方法上 from django.utils.decorators import method_decorator class ArticleView: @method_decorator(login_required) def edit(self, request): pass 装饰器的常见问题 # 1. 装饰器改变函数签名 # # 问题 @decorator def func(x, y): return x + y import inspect print(inspect.signature(func)) # 显示 wrapper 的签名 # 解决 import functools def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) # 或手动更新签名 wrapper.__signature__ = inspect.signature(func) return wrapper 2. 装饰器带可选参数 # # 问题：如何让装饰器参数可选？ def decorator(func=None, *, option=True): def actual_decorator(f): @functools.wraps(f) def wrapper(*args, **kwargs): if option: print(\u0026#34;选项启用\u0026#34;) return f(*args, **kwargs) return wrapper if func is None: return actual_decorator else: return actual_decorator(func) # 使用方式一：带参数 @decorator(option=False) def func1(): pass # 使用方式二：不带参数 @decorator def func2(): pass # 使用方式三：括号调用 @decorator() def func3(): pass 3. 类方法装饰器 # class MyClass: def __init__(self): self._value = 0 @property @decorator def value(self): \u0026#34;\u0026#34;\u0026#34;正确的装饰顺序\u0026#34;\u0026#34;\u0026#34; return self._value # @decorator # 错误顺序 # @property # def value(self): # return self._value 总结 # 装饰器语法总结 # # 基本装饰器 @decorator def func(): pass # 等价于 def func(): pass func = decorator(func) # 带参数的装饰器 @decorator(arg) def func(): pass # 等价于 def func(): pass func = decorator(arg)(func) # 多层装饰器 @decorator1 @decorator2 def func(): pass # 等价于 func = decorator1(decorator2(func)) 装饰器执行顺序 # 先执行最靠近函数的装饰器 由内到外执行 调用函数时反向执行 最佳实践 # 始终使用 @functools.wraps - 保留原函数元数据 使用 *args, **kwargs - 传递任意参数 装饰器工厂函数 - 实现带参数的装饰器 类装饰器 - 适合需要保存状态的装饰器 文档说明 - 为装饰器添加清晰的文档 常用场景 # 场景 装饰器类型 日志记录 函数装饰器 性能计时 函数装饰器 缓存 函数装饰器 权限验证 函数/类装饰器 参数验证 函数装饰器 重试机制 函数装饰器 路由注册 函数装饰器 装饰器是 Python 中非常强大的特性，掌握它可以让你的代码更加简洁、可复用和优雅！\n📚 推荐阅读\nPEP 318 - Decorators Python decorators documentation Real Python: Decorators ","date":"2026-04-22","externalUrl":null,"permalink":"/posts/python/python-decorators/","section":"文章","summary":"","title":"Python 装饰器基础：理解与使用","type":"posts"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/pythonic/","section":"标签","summary":"","title":"Pythonic","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/requests/","section":"标签","summary":"","title":"Requests","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/","section":"标签","summary":"","title":"标签","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/","section":"标签","summary":"","title":"错误处理","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E8%B0%83%E8%AF%95/","section":"标签","summary":"","title":"调试","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7/","section":"标签","summary":"","title":"高级特性","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/","section":"标签","summary":"","title":"函数式编程","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E5%88%97%E8%A1%A8%E6%8E%A8%E5%AF%BC%E5%BC%8F/","section":"标签","summary":"","title":"列表推导式","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E6%97%A5%E5%BF%97/","section":"标签","summary":"","title":"日志","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E7%94%9F%E6%88%90%E5%99%A8/","section":"标签","summary":"","title":"生成器","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/","section":"标签","summary":"","title":"网络编程","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/posts/","section":"文章","summary":"","title":"文章","type":"posts"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86/","section":"标签","summary":"","title":"异常处理","type":"tags"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/%E8%A3%85%E9%A5%B0%E5%99%A8/","section":"标签","summary":"","title":"装饰器","type":"tags"},{"content":"","date":"2026-04-21","externalUrl":null,"permalink":"/tags/blowfish/","section":"标签","summary":"","title":"Blowfish","type":"tags"},{"content":"","date":"2026-04-21","externalUrl":null,"permalink":"/tags/hugo/","section":"标签","summary":"","title":"Hugo","type":"tags"},{"content":"","date":"2026-04-21","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2/","section":"标签","summary":"","title":"博客","type":"tags"},{"content":" 蓝莓的故事 # 如果蓝莓可以长得和猪一样大就好了~~\n","date":"2026-04-21","externalUrl":null,"permalink":"/posts/one-day/","section":"文章","summary":"","title":"此帖注水","type":"posts"},{"content":"","date":"2026-04-21","externalUrl":null,"permalink":"/categories/%E6%9D%82%E8%B0%88/","section":"Categories","summary":"","title":"杂谈","type":"categories"},{"content":"","date":"2026-04-20","externalUrl":null,"permalink":"/","section":"饿梦博客","summary":"","title":"饿梦博客","type":"page"},{"content":" 前言 # 最近把之前的博客方案做了个替换，感觉还是这类博客比较流行一点，所以做了这个博客\n第一篇文章 # 这是这篇博客的第一篇文章，这里就简单做个测试，让我们稍后见！\n如果你也在用 Hugo，欢迎交流！\n","date":"2026-04-20","externalUrl":null,"permalink":"/posts/first/","section":"文章","summary":"","title":"新的博客","type":"posts"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/links/","section":"Links","summary":"","title":"Links","type":"links"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","externalUrl":null,"permalink":"/authors/%E9%A5%BF%E6%A2%A6/","section":"Authors","summary":"","title":"饿梦","type":"authors"},{"content":"这里是饿梦的博客，博主是一名大三的学生，立志于DevOps。\n这其实是我第二个博客，之前博客在云服务上放着，本来是在按部就班的学习，看到这类博客很漂亮，所以搬家了，\n发的文章内容取决于我想起来什么，目前对于写文章还不是很擅长，争取以后分享一些技术问题。\n","externalUrl":null,"permalink":"/about/","section":"饿梦博客","summary":"","title":"关于","type":"page"},{"content":"QQ Email: 1710233908@qq.com\n163 Email: hungerdream@163.com\n","externalUrl":null,"permalink":"/links/email/","section":"Links","summary":"","title":"邮箱","type":"links"}]