会报恩的代码(一)
引言
在 Python 编程中,有一些技巧和最佳实践可以帮助你编写更优雅、更高效的代码。本文将介绍 Python 中的 yield
和 yield from
用法,以及处理 offset-naive
和 offset-aware
的 datetime
对象时的最佳实践。此外, 我们还将探讨如何处理 requests
异常,以提高代码的鲁棒性和用户体验。
yield 与 yield from
在 Python 中,yield
和 yield from
都用于生成器,但它们有不同的用途和行为。
yield
- 用于定义生成器函数,返回一个值并暂停执行。
- 每次调用生成器的
__next__()
方法时,将从上次暂停的地方继续执行,直到下一个yield
语句。 - 可以简单地生成值。
示例:
def simple_generator():
yield 1
yield 2
yield 3
gen = simple_generator()
print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
yield from
- 用于委派生成器,允许一个生成器调用另一个生成器。
- 自动处理了子生成器
yield
的返回值,并将所有yield
的值传递给调用者,从而简化了生成器的嵌套结构。 - 在使用
yield from
后,外层生成器会等待内层生成器完成。
示例:
def sub_generator():
yield 1
yield 2
def main_generator():
yield from sub_generator()
yield 3
gen = main_generator()
print(list(gen)) # 输出: [1, 2, 3]
关键区别
-
用法:
yield
用于单个生成器。yield from
用于从一个生成器委托给另一个生成器。
-
返回值:
yield
不处理返回值。yield from
可以从子生成器接收返回值并传播。
-
代码简洁性:
- 使用
yield from
可以使代码更简洁,减少嵌套生成器的复杂性。
- 使用
总结来说,yield
是基本的生成器功能,而 yield from
用于处理更复杂的生成器之间的关系,使代码更清晰和易于管理。
示例:嵌套生成器
def flatten_contents(contents, role_type, category, parent_content_name=''):
"""
解析压平 contents
:param contents: 当前节点的 Content
:param role_type: 1 - A; 2 - B; 3 - C; 4 - Ds;
:param category: 1 - A; 2 - B;
:param parent_content_name: 父节点名称
:return: {content_id, content_name, content_type, parent_content_name, role_type, category}
"""
for content in contents:
if int(content['content_type']) == 1:
yield from flatten_contents(
content.get('sub_contents',[]),
role_type,
category,
content.get('content_name','')
)
elif int(content['content_type']) in [2, 3]:
yield {
'content_id': content.get('content_id', ''),
'content_name': content.get('content_name', ''),
'content_type': content.get('content_type', ''),
'parent_content_name': parent_content_name,
'role_type': role_type,
'category': category,
}
offset-naive
and offset-aware
datetimes
Python 中的 datetime
模块提供了两种类型的日期时间对象:offset-naive
和 offset-aware
,如果 直接对两个不同的对象进行比较,会抛出 TypeError:
TypeError: can't compare offset-naive and offset-aware datetimes
这个错误是由于尝试比较一个“无时区”(offset-naive)和一个“有时区”(offset-aware)的 datetime
对象引起的。要解决这个问题,需要确保两者都具有相同的时区信息。
以下是解决此问题的步骤:
-
确保输入是 offset-aware:在创建
datetime
对象时,使用timezone.utc
为其指定时区。 -
将 offset-naive 的
datetime
转换为 offset-aware:如果你的其他datetime
对象是 offset-naive,你需要将其转换成 offset-aware。
修复示例
假设你有一个“无时区”的 datetime
对象 naive_dt
,你希望将其与 datetime.fromtimestamp
得到的有时区对象进行比较:
from datetime import datetime, timezone
# 假设 naive_dt 是一个无时区的 datetime 对象
naive_dt = datetime(2023, 10, 1) # 示例的 naive datetime
# 将 naive_dt 转换为 offset-aware datetime
naive_dt = naive_dt.replace(tzinfo=timezone.utc)
# 假设 updated_at 是一个毫秒级时间戳
updated_at = 1724330343000
# 比较 updated_at 和 naive_dt
updated_at = datetime.fromtimestamp(int(str(updated_at)[:10]), tz=timezone.utc)
if naive_dt < updated_at:
print("naive_dt is earlier than updated_at")
确保所有 datetime
对象都保持一致的时区信息,通常使用 UTC 可以避免此类问题。
requests 最佳实践
在处理 requests.exceptions.ConnectionError
异常(比如 ConnectionResetError
)时,可以采取以下最佳实践,以提高代码的鲁棒性和用户体验:
异常捕获
使用 try-except
块捕获异常,使程序能优雅地处理错误。
import requests
try:
response = requests.get('https://example.com')
response.raise_for_status() # 检查 HTTP 响应状态
except requests.exceptions.ConnectionError as e:
print(f"Connection error: {e}")
except requests.exceptions.HTTPError as e:
print(f"HTTP error: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
重试逻辑
如果遇到暂时性的连接错误,可以实现重试机制。例如使用 Retry
来进行简单的重试。
from requests.adapters import HTTPAdapter, Retry
session = requests.Session()
retry = Retry(
total=5, # 最多重试 5 次
backoff_factor=1, # 每次重试之间的等待时间倍增
status_forcelist=[500, 502, 503, 504], # 重试的 HTTP 状态码
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('https://', adapter)
try:
response = session.get('https://example.com')
response.raise_for_status()
except requests.exceptions.ConnectionError as e:
print(f"Connection error: {e}")
except requests.exceptions.HTTPError as e:
print(f"HTTP error: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
日志记录
为产生的异常添加日志记录,以方便后续的故障排查。
import logging
logging.basicConfig(level=logging.INFO)
try:
response = session.get('https://example.com')
response.raise_for_status()
except requests.exceptions.ConnectionError as e:
logging.error(f"Connection error: {e}")
except requests.exceptions.HTTPError as e:
logging.error(f"HTTP error: {e}")
except Exception as e:
logging.error(f"An unexpected error occurred: {e}")
最后的处理
确定在重试失败后如何处理,例如返回默认值、抛出自定义异常或退出程序等。
if response is None:
# 处理失败的请求,例如返回默认值
response = {'data': None, 'error': 'Failed to retrieve data'}
以上步骤结合使用将有助于实现良好的错误处理机制,并提升用户体验。
结语
通过使用 yield
和 yield from
,你可以更好地管理生成器之间的关系,使代码更加简洁和易于理解。同时,处理 offset-naive
和 offset-aware
的 datetime
对象时,确保它们具有相同的时区信息,以避免出现 TypeError
异常。在处理 requests
异常时,使用异常捕获、重试逻辑、日志记录和最后的处理等最佳实践,可以提高代码的鲁棒性和用户体验。
PS:感谢每一位志同道合者的阅读,欢迎关注、点赞、评论!