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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 01:22:37