Unity+Pythonnet:PlayMode切换时无法重置sys.modules状态求助
Unity中pythonnet集成:PlayMode切换后重置sys.modules的问题
问题背景
使用NuGet包pythonnet将Python集成到Unity项目,采用了Python生命周期管理逻辑(对应PythonLifeCycle.cs)。运行时通过以下代码导入模块:
using (Py.GIL()) { using (PyModule scope = Py.CreateScope()) { dynamic sys = scope.Import("sys"); Debug.Log(sys.modules.keys()); sys.path.append("C:\\Users\\<user>\\my-package"); dynamic pythonPackage = scope.Import("my_package"); sys.path.append(packagePath); scope.Import("src"); this.module = scope.Import("src.main"); } }
退出并重新进入PlayMode后,初始化Python引擎时打印sys.modules,发现全局sys.modules中仍保留my_package,说明Py.CreateScope()的隔离作用无效。已尝试以下方案但均失败:
- 使用
importlib.reload重载模块 - 场景启动时执行
InitializeEngine() -> ShutdownEngine() -> InitializeEngine() - 完整复制对应项目的Python配置(包括设置
PYTHONDONTWRITEBYTECODE=1环境变量) - 移除pythonnet代码中
RestoreRuntimeData的所有引用
只有重启Unity编辑器才能重置sys.modules,需找到PlayMode切换时重置sys.modules的有效方法。
可行解决方案
1. 手动清理自定义模块
在Python引擎初始化完成后、导入新模块前,遍历sys.modules并移除自定义模块及相关依赖(避免删除Python内置模块):
using (Py.GIL()) { dynamic sys = Py.Import("sys"); var modulesToRemove = new List<string>(); foreach (var moduleName in sys.modules.keys()) { string name = moduleName.ToString(); if (name.StartsWith("my_package") || name.StartsWith("src")) { modulesToRemove.Add(name); } } foreach (var name in modulesToRemove) { sys.modules.Remove(name); } }
2. 彻底释放Python引擎资源
Unity的PlayMode切换时进程并未重启,pythonnet的Python runtime可能残留内存。可在PlayMode切换回调中执行彻底清理:
#if UNITY_EDITOR using UnityEditor; #endif private void OnApplicationQuit() { ShutdownPythonEngine(); } #if UNITY_EDITOR [InitializeOnLoadMethod] static void Init() { EditorApplication.playModeStateChanged += OnPlayModeChanged; } static void OnPlayModeChanged(PlayModeStateChange state) { if (state == PlayModeStateChange.ExitingPlayMode) { PythonLifeCycle.ShutdownEngine(); // 强制触发垃圾回收 GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } } #endif
同时修改ShutdownEngine()方法,强化清理逻辑:
public static void ShutdownEngine() { if (!IsInitialized) return; using (Py.GIL()) { dynamic sys = Py.Import("sys"); // 只清理自定义模块,避免影响Python内置功能 var modulesToRemove = new List<string>(); foreach (var moduleName in sys.modules.keys()) { string name = moduleName.ToString(); if (name.StartsWith("my_package") || name.StartsWith("src")) { modulesToRemove.Add(name); } } foreach (var name in modulesToRemove) { sys.modules.Remove(name); } } Py.Shutdown(); IsInitialized = false; }
3. 子进程隔离Python环境
如果上述方法无效,可将Python代码放到独立子进程运行,通过IPC(命名管道、Socket等)与Unity通信。每次进入PlayMode启动新的Python进程,天然隔离环境,无sys.modules残留问题。
内容的提问来源于stack exchange,提问作者INTODAN
相关产品推荐
相关产品推荐

