# 转 UTC 存储 utc_time = beijing_time.astimezone(ZoneInfo("UTC")) print("存储用:", utc_time.isoformat())
# 展示给不同参与者 zones = [ "Asia/Shanghai", "Asia/Tokyo", "Europe/London", "America/New_York", "America/Los_Angeles", ] for z in zones: print(format_for(utc_time, z))
正则表达式(regex)是文本处理的瑞士军刀。你能用它一行代码解决”从日志里提取所有 IP”、”验证邮箱格式”、”把驼峰命名改成蛇形”、”从 HTML 里抽出所有链接”(虽然 HTML 更该用 BeautifulSoup)。Python 的正则由 re 模块提供。这一篇不做完整语法参考(那要一整本书),而是把日常最常用的模式和 API 讲清楚。
deflog(func): @functools.wraps(func) defw(*args, **kw): print(f"log begin {func.__name__}") r = func(*args, **kw) print(f"log end {func.__name__}") return r return w
deftimed(func): @functools.wraps(func) defw(*args, **kw): print(f"timer start") r = func(*args, **kw) print(f"timer end") return r return w
@log @timed defwork(): print("working")
work() # log begin work # timer start # working # timer end # log end work
defrequire_role(role): defdecorator(func): @functools.wraps(func) defwrapper(*args, **kwargs): if role notin current_user["roles"]: raise PermissionError() return func(*args, **kwargs) return wrapper return decorator
参数校验
1 2 3 4 5 6 7 8
defvalidate_positive(func): @functools.wraps(func) defwrapper(*args, **kwargs): for a in args: ifisinstance(a, (int, float)) and a < 0: raise ValueError(f"参数不能为负:{a}") return func(*args, **kwargs) return wrapper
结果缓存(带 TTL)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import time from functools import wraps
defttl_cache(ttl=60): defdecorator(func): cache = {} @wraps(func) defwrapper(*args, **kwargs): key = (args, tuple(sorted(kwargs.items()))) now = time.time() if key in cache: val, ts = cache[key] if now - ts < ttl: return val val = func(*args, **kwargs) cache[key] = (val, now) return val return wrapper return decorator
defread_events(lines): """lines 是行迭代器;产出一批批事件(一批 = 空行前的所有行)。""" buffer = [] for line in lines: line = line.rstrip() ifnot line: if buffer: yield buffer buffer = [] else: buffer.append(line) if buffer: yield buffer
defenrich(events): """给每个事件加上一个 id。""" for i, ev inenumerate(events, 1): yield {"id": i, "lines": ev}
defkeep_errors(events): """只保留含 ERROR 关键字的事件。""" for ev in events: ifany("ERROR"in ln for ln in ev["lines"]): yield ev
if __name__ == "__main__": sample = [ "INFO start", "INFO loading", "", "ERROR db failure", "INFO retry", "", "INFO shutdown", ] pipeline = keep_errors(enrich(read_events(iter(sample)))) for e in pipeline: print(e) # {'id': 2, 'lines': ['ERROR db failure', 'INFO retry']}
for x in obj: 背后到底发生了什么?为什么列表、字典、字符串、文件、range 都能这么用?答案就是 Python 的迭代协议——__iter__ 和 __next__。理解迭代器,你就能写出能被 for 循环、内置函数(sum/max/any 等)无缝使用的自定义类,也能明白生成器(下一篇)为什么那么强大。
with open(...) as f: 是每个 Python 学习者的必经之路。但 with 的用途远不止”打开文件”——数据库连接、锁、临时切换目录、性能计时……几乎所有”需要成对进入/退出”的场景都能用它。这一篇讲清楚 with 背后的上下文管理器协议(__enter__ / __exit__),以及现代 Python 提供的 contextlib 工具箱。
一、with 是什么
with 语句保证”进入时执行一段代码、退出时(无论正常还是异常)执行另一段代码”。最经典的用途是自动关闭文件:
1 2 3
withopen("data.txt", encoding="utf-8") as f: data = f.read() # 到这里 f 已经自动关闭了
等价于:
1 2 3 4 5
f = open("data.txt", encoding="utf-8") try: data = f.read() finally: f.close()
with 更简洁、更不容易漏掉 close。
二、上下文管理器协议
任何实现了 __enter__ 和 __exit__ 方法的对象,都是上下文管理器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classTimer: def__enter__(self): import time self.start = time.perf_counter() returnself# 会赋给 as 后的名字
with transaction(conn) as tx: tx.execute("...") tx.execute("...")
3. 加锁
1 2 3 4 5 6 7
import threading
lock = threading.Lock()
with lock: # 独占访问共享资源 critical_section()
threading.Lock 已经实现了上下文管理器协议,直接用。
4. 环境变量临时替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
@contextmanager defenv_var(name, value): old = os.environ.get(name) os.environ[name] = value try: yield finally: if old isNone: os.environ.pop(name, None) else: os.environ[name] = old
with env_var("DEBUG", "1"): run_test() # 出来后 DEBUG 恢复原状
# Top IP CSV with (out_dir / "top_ips.csv").open("w", encoding="utf-8", newline="") as f: writer = csv.writer(f) writer.writerow(["ip", "count"]) writer.writerows(report["top_ips"])
if __name__ == "__main__": log_path = Path("access.log") ifnot log_path.exists(): # 造一份测试数据 log_path.write_text( "2025-09-21 10:00:00 GET /a 200 1.1.1.1\n" "2025-09-21 10:00:01 GET /b 200 1.1.1.1\n" "2025-09-21 10:00:02 GET /a 404 2.2.2.2\n" "2025-09-21 10:00:03 GET /c 200 1.1.1.1\n", encoding="utf-8", ) report = analyze(log_path) save_report(report, Path("out")) print(report)
defload_config(path): try: withopen(path) as f: return json.load(f) except FileNotFoundError as e: raise ConfigError(f"配置文件 {path} 不存在") from e except json.JSONDecodeError as e: raise ConfigError(f"配置文件 {path} 格式错误") from e
defwith_retry(func, times=3, delay=1): for i inrange(times): try: return func() except Exception as e: if i == times - 1: raise print(f"重试 {i+1}/{times}:{e}") time.sleep(delay)
defread_int(prompt: str, min_val: int = None, max_val: int = None) -> int: whileTrue: raw = input(prompt).strip() try: n = int(raw) except ValueError: print("请输入整数") continue
if min_val isnotNoneand n < min_val: print(f"必须 >= {min_val}") continue if max_val isnotNoneand n > max_val: print(f"必须 <= {max_val}") continue return n
defsafe_divide(a, b): """安全除法。""" try: return a / b except ZeroDivisionError: logger.warning(f"除零:{a}/{b}") returnfloat("inf") if a > 0elsefloat("-inf") except TypeError as e: raise TypeError(f"不能除:{a!r} / {b!r}") from e
if __name__ == "__main__": age = read_int("年龄(1-150):", min_val=1, max_val=150) print(f"你 {age} 岁")