Python 中 Unicode 正规化的边缘情况
Python 中 Unicode 正规化的边缘情况很多 Python 开发者觉得:unicodedata.normalize()只是:“把 Unicode 字符统一格式”但实际上:Unicode 正规化(Normalization)远比想象中复杂。尤其:用户名比较搜索引擎数据库存储文件系统URL安全过滤OCR爬虫里。很多“看起来一样”的字符串:实际上并不相等一、最经典问题:视觉相同,但字符串不同例
functools.partial 与方法绑定的非直觉行为
functools.partial 与方法绑定的非直觉行为很多 Python 开发者觉得:functools.partial只是:“提前固定几个参数”但实际上:它和 Python 的“方法绑定机制”组合后,会出现很多非常反直觉的行为。尤其涉及:实例方法classmethodstaticmethoddescriptorself 自动绑定时。很多线上 bug 都和这里有关。一、先理解:Python 方
使用 namedtuple 的不可变性陷阱
使用 namedtuple 的不可变性陷阱很多 Python 开发者第一次接触:from collections import namedtuple时,会认为:namedtuple = 不可变对象但实际上:它只是“表面不可变”。很多场景下,它的行为会比普通 tuple 更容易踩坑。一、最经典误区:字段不可变 ≠ 内部对象不可变例如:from collections import namedtupl
python asyncio.sleep 精度问题的微妙影响
一、最常见的误解:sleep 并不会精准等待很多人以为:await asyncio.sleep(1)一定会在 1 秒后恢复执行。实际上:它只保证“至少等待 1 秒”。真正恢复执行的时间,取决于:事件循环是否空闲当前任务数量系统调度CPU 忙碌程度selector/poll 精度操作系统时钟粒度因此:import asyncioimport timeasync def test(): &
Python 中的垃圾回收延迟与资源泄漏
一、为什么会“延迟回收”?Python 有两套机制:1️⃣ 引用计数(即时)引用数归零 → 立刻销毁 → 可能触发 __del__2️⃣ 垃圾回收器 GC(延迟)专门处理循环引用,触发时机不确定(阈值驱动)👉 结论:❗只要涉及循环引用或 GC,回收就可能延迟二、延迟 ≠ 内存泄漏,但会变成资源泄漏看一个典型例子:import gcclass FileHolder: def
@staticmethod 与继承类方法的边缘问题
@staticmethod 在继承体系里确实有一些边缘行为,很多看起来“能用”,但在扩展或多态时会出坑。下面把关键问题和实战建议讲清楚。一、核心本质(先定调)class A: @staticmethod def foo(): print("A.foo")👉 @staticm
pathlib 与符号链接的潜在异常
pathlib 在处理**符号链接(symlink)**时确实有不少“暗坑”,尤其是涉及 .resolve()、文件操作、权限、跨平台行为时,很容易踩异常或得到和预期不一致的结果。下面把常见异常 + 触发场景 + 正确用法给你讲透。一、最常见异常一览1️⃣ FileNotFoundError(断链)from pathlib import Pathp = Path("broken_link
Python 中循环引用导致的 __del__ 不调用
一、问题本质:为什么 __del__ 不执行?Python 主要用两种机制回收对象:1️⃣ 引用计数(Reference Counting)对象引用数变成 0 → 立刻销毁 → 调用 __del__2️⃣ 垃圾回收器(GC,处理循环引用)用于处理这种情况:a = A()b = B()a.b = bb.a = a👉 这就是循环引用⚠️ 关键点来了:👉 带有 __del__ 的对象,如果存在循环引用,
使用 weakref.finalize 释放资源的微妙问题
Python 的 weakref.finalize 经常被当成:“比 __del__ 更安全的资源释放方案”确实它解决了:循环引用导致 __del__ 不执行GC 顺序问题对象销毁崩溃但它本身也有很多非常隐蔽的边缘行为。尤其:文件句柄socketSelenium driverasyncio 对象ctypes 资源数据库连接临时文件这些场景里很容易踩坑。1. finalize 并不保证立即执行很多人
float 精度问题在边缘场景中的奇异表现
Python 的 float 本质是:IEEE 754 双精度浮点数(64 位)它不是“真实小数”,而是:符号位 + 指数位 + 尾数位因此很多十进制数字根本无法被精确表示。大多数人知道:0.1 + 0.2 != 0.3但真正危险的,其实是一些“边缘场景”的奇异行为。这些问题:不容易复现不一定报错看起来像 Python Bug实际是浮点底层设计导致1. 0.1 + 0.2 只是最基础的问题经典例子
datetime 模块时区处理的潜在陷阱
Python 的 datetime 模块最容易出问题的地方,不是语法,而是:“它很多行为看起来正常,但实际在跨时区、夏令时、时间比较时会 silently 出错。”尤其是:日志系统爬虫定时任务数据库存储分布式系统Selenium 自动化API 时间戳转换这些场景里,时区坑非常多。1. naive datetime 与 aware datetime 混用最经典的问题:from datetime im
Python 内置函数缓存的不一致行为
1. id() 看起来“不唯一”很多人以为:id(x)返回的是“唯一”的对象编号。实际上它只保证:在对象生命周期内唯一。对象销毁后,内存地址可能复用。例如:a = [1, 2]print(id(a))del ab = [3, 4]print(id(b))有时候两个 id 会一样。因为:CPython 的 id() 本质是内存地址Python 有对象池和内存复用机制所以:id() != 全局唯一ID
ctypes 调用 C 库时的内存管理问题
在 Python 里用 ctypes 调用 C 库是非常方便的,但同时也是 内存管理的高风险区。这里的问题主要源自:Python 自动管理内存(垃圾回收)C 需要手动管理内存两者交互时容易产生 悬空指针、内存泄漏或双重释放我把核心问题梳理一下。1. Python 对 ctypes 的对象管理基本情况import ctypeslib = ctypes.CDLL("./mylib.so&qu
Python 中的多重继承钻石问题
Python 的“钻石继承(Diamond Inheritance)”是多重继承里最经典的问题之一。它最大的难点不是语法。而是:多个父类之间到底该按什么顺序初始化和调用方法。很多人第一次接触时都会被:super()MRO重复初始化方法调用顺序绕晕。什么是钻石继承结构像这样: A / \&nbs
python sys.settrace 对性能的意外影响
sys.settrace() 是 Python 里一个非常“底层”的调试钩子。很多人只知道:调试器会用它coverage 会用它profile 工具会用它但真正容易踩坑的是:只要启用了 trace,整个 Python 解释器的执行模型都会改变。而且影响远比很多人想象的大。sys.settrace() 是什么最简单例子:import sysdef tracer(frame, event, arg):
python asyncio.gather 中异常吞噬的问题
asyncio.gather() 是 Python 异步编程里非常常见的并发工具,但它有一个很容易踩坑的点:异常处理行为并不直观。很多人以为某个协程报错后一定会立刻抛出异常,但实际上可能出现:异常被“吞掉”其他任务继续运行某些异常永远没人处理日志里出现 Task exception was never retrieved下面把几个核心问题拆开讲。asyncio.gather() 的默认行为先看最常
Python 中使用负数步长切片的非直观行为
1️⃣ 基础回顾:切片语法seq[start:stop:step]start:起始索引(包含)stop:结束索引(不包含)step:步长(默认 1,可为负数)负数索引允许从序列尾部开始计数核心规则:向前切片(step>0):从 start → stop(不含 stop)向后切片(step<0):从 start → stop(不含 stop,方向倒过来)2️⃣ 负数步长的微妙行为(a)默
python中setattr 与 __slots__ 的冲突
1️⃣ 背景:__slots__ 的作用Python 中普通类的实例有一个 __dict__ 存储属性:class A: passa = A()a.x = 10 # 存在 __dict__print(a.__dict__) # {'x': 10}问题:如果类属性多、实例多,__dict__ 占用内存大。解决方案:使用 __slots
Python 生成器在异常传播中的边缘行为
1️⃣ 基础:生成器的异常传播在 Python 中,生成器是一种 可迭代对象,可以暂停和恢复执行:def gen(): yield 1 yield 2当异常在生成器内部发生时,如果没有捕获,会 传播到外部迭代者:def gen(): yield 1 raise ValueError("
importlib.reload 对模块状态的微妙影响
1️⃣ 基本行为importlib.reload(module) 的作用:重新执行模块的 顶层代码(即模块文件中的所有语句)。返回模块对象本身(与原来的模块对象同一个引用)。不会重新创建模块对象,所以原来的模块引用仍然有效。import mymoduleimport importlibmymodule.x = 10importlib.reload(mymodule)print(mymodule.x