纯函数指仅依赖输入参数、返回值完全由参数决定且无任何可观察副作用的函数;测试时需验证同一输入多次调用结果恒定、互不干扰,并避免隐式状态、非确定性调用及闭包捕获可变依赖。

纯函数在测试中确实更省心,但前提是它真被写成了纯函数——很多人误以为没改全局变量就算纯,其实不是。
什么是纯函数:看 return 和 side_effect
纯函数只依赖输入参数,返回值完全由参数决定,且不产生任何可观察的副作用(比如改 list、写文件、调用 print()、修改 self 属性)。测试时你喂它一组输入,它必须每次都吐出相同输出,否则就不是纯的。
常见错误现象:test_my_func(1) == 2 第一次过,第二次失败;原因可能是函数内部用了 random.random() 或缓存了上次结果到模块级变量。
- 检查函数体里有没有调用非确定性函数:
time.time()、uuid.uuid4()、os.getenv()(除非你确保环境一致) - 避免隐式状态:不要用
cache = {}这种模块级字典做记忆化,改用@functools.lru_cache并显式控制 - 如果函数接收可变对象(如
list),别直接修改它——用sorted(items)而不是items.sort()
测试时怎么验证它真是纯的
不能光看代码,得靠测试反推。核心思路是:同一输入反复调用,输出必须恒定;且多次调用之间不能互相干扰。
立即学习“Python免费学习笔记(深入)”;
这本书给出了一份关于python这门优美语言的精要的参考。作者通过一个完整而清晰的入门指引将你带入python的乐园,随后在语法、类型和对象、运算符与表达式、控制流函数与函数编程、类及面向对象编程、模块和包、输入输出、执行环境等多方面给出了详尽的讲解。如果你想加入 python的世界,David M beazley的这本书可不要错过哦。 (封面是最新英文版的,中文版貌似只译到第二版)
使用场景:单元测试里对关键计算逻辑(比如价格计算、数据清洗规则)做稳定性断言。
- 写个简单循环跑 5 次:
assert my_calc(3.5) == my_calc(3.5)—— 如果失败,说明有隐藏状态或外部依赖 - 传入相同可变对象两次:
data = [1, 2, 3]; a = process(data); b = process(data); assert a == b—— 若process修改了data,第二次调用可能行为不同 - 用
pytest的monkeypatch拦截可疑依赖,比如把datetime.datetime.now替换成固定返回值,再测
lambda 和 functools.partial 容易踩的坑
它们看起来“没副作用”,但很容易意外捕获外部变量,变成事实上的非纯函数。
性能影响:纯函数更容易被缓存或并行化,但如果你用 partial 绑定了一个正在变化的对象引用(比如某个类实例),那每次调用实际执行的是不同逻辑。
-
lambda x: x + CONFIG['tax_rate']不纯——CONFIG可能在测试中途被改写 -
partial(my_func, db_conn=conn)不纯——conn是可变对象,且带状态 - 安全做法:只绑定不可变值(
int、str、tuple),或把依赖显式作为参数传入,而不是闭包捕获
真正难的不是写出纯函数,而是守住它的边界——尤其当它被嵌套在类方法里、或和配置/日志/监控混在一起时,副作用会悄悄渗进来。多跑几遍测试,比读十遍定义管用。









