You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyEval_EvalCode对参数字典的引用计数增减规则咨询

关于PyEval_EvalCode中全局/局部字典的引用计数管理

我来帮你拆解这个问题——我之前在把Python嵌入到C项目里的时候也踩过类似的引用计数坑,官方文档确实在这个细节上语焉不详,得结合源码和实际行为来梳理。

核心认知:先明确引用计数的基本规则

在Python C API里,引用计数遵循**「谁创建/谁显式持有,谁负责释放」**的原则:

  • 如果你通过PyDict_New()、PyDict_Copy()这类函数创建了字典对象,你会获得这个对象的一个拥有引用,必须在不再使用时调用Py_DECREF()。
  • 如果是通过PyEval_GetGlobals()、PyModule_GetDict()这类API获取的字典,这是一个借用引用,你不需要负责释放,除非你显式调用了Py_INCREF()把它变成拥有引用。

PyEval_EvalCode对字典的实际引用行为

针对你提到的两种脚本场景,差异的核心在于脚本是否创建了持有全局/局部字典的对象:

1. 场景一:包含空函数的脚本

当脚本定义了函数时,Python会自动让函数的__globals__属性指向你传入的全局字典,这会给全局字典增加一个永久引用(只要函数对象存在,这个引用就会保留)。这时候:

  • 你传入的全局字典的引用计数会比执行脚本前多1(来自函数对象)。
  • 如果之后你手动对这个字典调用Py_DECREF(),引用计数不会降到0(因为函数还持有它),但如果函数对象被回收(比如没有其他引用),字典的引用计数才会减少。

2. 场景二:无任何操作的脚本

这种情况下,脚本没有创建任何持有全局/局部字典的对象,所以:

  • PyEval_EvalCode内部只会临时对传入的字典做Py_INCREF()和Py_DECREF(),执行完毕后字典的引用计数会回到调用前的状态。

何时需要手动调用Py_DECREF?

根据不同的字典来源,分两种情况:

  • 如果你自己创建了全局/局部字典(比如用PyDict_New()):
    不管脚本执行内容是什么,你都需要在不再使用这个字典时调用Py_DECREF()——但要注意:如果脚本创建了持有该字典的对象(比如函数、类),你需要确保这些对象已经被回收后再释放字典,否则会出现悬垂指针(字典被销毁,但还有对象引用它)。
  • 如果你使用的是Python自带的字典(比如模块的全局字典、当前帧的全局字典):
    这些是借用引用,你不需要手动调用Py_DECREF(),除非你之前显式调用了Py_INCREF()来持有它。

避免悬垂问题的关键注意点

  • 不要提前释放还有其他对象引用的字典:比如脚本定义了函数,函数还在被使用时,不要释放它的__globals__指向的字典。
  • 不要对借用的引用调用Py_DECREF():比如从PyModule_GetDict()拿到的模块字典,如果你没Py_INCREF()过,调用Py_DECREF()会直接导致字典被错误销毁,引发悬垂。
  • 可以用Py_REFCNT()宏(调试时)查看字典的引用计数变化,确认是否有额外的引用被持有。

内容的提问来源于stack exchange,提问作者HelloWorld

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:08:27