核心思路是切断不同执行单元对同一命名空间的共享:通过独立进程/docker隔离、每次exec前新建globals字典、threading.local或contextvars绑定状态、测试中用fixture重载或恢复模块变量。

核心思路是切断不同执行单元对同一命名空间的共享。变量污染本质不是变量本身的问题,而是多个逻辑本该互不干扰,却被迫共用一个 globals() 或模块级作用域。
用独立进程或容器彻底隔离
这是最彻底的方案,适用于在线评测、多租户沙箱等场景:
- 每个用户代码在独立子进程中运行,进程结束即销毁全部状态
- Docker 容器进一步隔离文件系统、网络和环境变量,避免跨任务残留
- Python 的
subprocess.run()或multiprocessing.Process可直接实现,无需修改业务逻辑
为每次执行重置命名空间
在共享解释器但需顺序执行的环境(如 Jupyter Kernel、简易 exec 沙箱)中,主动控制 globals 字典:
- 不要复用同一个字典对象传给
exec();每次执行前新建空字典或只保留内置函数 - 示例:
namespace = {'__builtins__': __builtins__},再执行exec(code, namespace) - 若需保留部分基础模块(如
math),显式导入并赋值,而非依赖全局环境
按执行单元划分上下文边界
针对并发或异步场景,用语言原生机制绑定状态到具体执行流:
- 多线程:用
threading.local(),每个线程有独立属性空间 - 协程/asyncio:必须用
contextvars.ContextVar,threading.local在单线程多协程下完全失效 - 注意:ContextVar 要提前声明,读写必须通过
.get()和.set(),不能直接当普通变量用
测试中主动清理模块级状态
pytest 的 tmp_path 管文件,不管内存;模块级变量(如配置、日志器、缓存)需手动干预:
- 用
@pytest.fixture(autouse=True, scope="function")配合importlib.reload()重载关键模块 - 或更轻量:在 fixture 中保存原始值,
yield后恢复,例如config.timeout = original - 避免在 module/session 级 fixture 中做副作用操作,除非配对了明确的 teardown











