在C#项目中调用Python模块,通常可以采用托管式嵌入和独立进程调用两种思路。托管式嵌入借助Pythonnet把Python运行时加载到.NET进程内,适合频繁互调的场景;进程调用则通过启动python.exe执行脚本,适合解耦和隔离需求较强的业务。

一、使用Pythonnet进行内嵌调用
Pythonnet提供了PythonEngine类,可以让C#直接导入Python模块并调用其中的函数。使用前需通过NuGet安装Python.Runtime包,并确认本机已安装相同版本的Python。
1. 基本调用示例
以下代码演示了在C#中调用Python标准库math模块计算平方根:
using System;
using Python.Runtime;
class Program
{
static void Main()
{
// 初始化Python运行时,指定Python home路径
Runtime.PythonDLL = @"C:Python39python39.dll";
PythonEngine.Initialize();
using (Py.GIL()) // 获取全局解释器锁
{
dynamic math = Py.Import("math");
double result = math.sqrt(16.0);
Console.WriteLine("sqrt(16) = " + result);
}
PythonEngine.Shutdown();
}
}
2. 调用自定义模块
将自定义Python文件放到程序运行目录,或加入sys.path后即可导入:
# mymodule.py
def add(a, b):
return a + b
using (Py.GIL())
{
dynamic sys = Py.Import("sys");
sys.path.append("."); // 添加当前目录
dynamic mod = Py.Import("mymodule");
int sum = mod.add(3, 5);
Console.WriteLine(sum);
}
二、通过进程调用Python脚本
如果不希望将Python运行时嵌入到C#进程,可以用Process类启动外部Python解释器,通过标准输入输出交换数据。
1. 执行脚本并读取输出
using System.Diagnostics;
class Runner
{
public static string RunScript(string scriptPath, string args)
{
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = "python",
Arguments = $"{scriptPath} {args}",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (Process proc = Process.Start(psi))
{
string output = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
return output;
}
}
}
对应的Python脚本可以这样写:
# calc.py
import sys
if __name__ == "__main__":
a = int(sys.argv[1])
b = int(sys.argv[2])
print(a * b)
三、方案对比与选择
| 方式 | 优点 | 缺点 |
|---|---|---|
| Pythonnet内嵌 | 调用延迟低,对象可直接互操作 | 版本依赖严格,排错复杂 |
| 进程调用 | 环境隔离好,部署简单 | 启动开销大,数据需序列化 |
四、常见注意事项
- Pythonnet要求C#目标平台与Python位数一致,均为x64或x86。
- 若提示找不到模块,请检查
sys.path是否包含脚本目录。 - 进程调用时建议对参数做转义,避免命令行注入。
根据项目规模和团队技术栈,选择合适的调用方式即可在C#中高效复用Python生态能力。