C#/.NET通过Python.NET调用Python脚本与本地环境集成
在Windows环境下开发应用时,结合C#强大的UI能力与Python丰富的AI及数据科学库是常见需求。本文介绍如何使用pythonnet(Python.NET)在.NET 6中无缝调用Python代码,包括本地环境封装、第三方库支持、对象交互及性能优化。
方案选择与环境准备
在.NET中调用Python主要有四种方式,pythonnet因兼具高交互性、支持第三方库且无需终端用户安装Python环境,成为成熟软件的首选方案。
| 方式 | 优点 | 缺点 |
|---|---|---|
| IronPython | 无需安装Python环境,无缝集成 | 不支持numpy等主流科学计算库 |
| C++动态链接库 | 交互性强 | 实现复杂,需用户配置Python环境 |
| 命令行调用 | 实现简单 | 交互性差,需用户配置环境 |
| 打包为EXE | 无Python依赖 | 体积大,数据传递复杂,性能损失大 |
以下步骤均在.NET 6环境下测试通过。首先确保已安装Python环境(建议Anaconda),并使用Nuget添加pythonnet引用。
第1步:配置运行环境与路径
核心在于初始化PythonEngine并指向具体的Python DLL及模块路径。为避免用户配置困扰,应将Python虚拟环境随程序分发。
string pathToVirtualEnv = ".\\envs\\pythonnetTest";
// 指定Python版本对应的dll,如python39.dll对应Python 3.9
Runtime.PythonDLL = Path.Combine(pathToVirtualEnv, "python39.dll");
PythonEngine.PythonHome = Path.Combine(pathToVirtualEnv, "python.exe");
// 设置Python模块搜索路径,site-packages必须放在最后以确保优先加载
PythonEngine.PythonPath = $"{pathToVirtualEnv}\\Lib\\site-packages;{pathToVirtualEnv}\\Lib";
PythonEngine.Initialize();
第2步:执行Python脚本与函数调用
所有Python代码执行必须包裹在using (Py.GIL())块内以获取全局解释器锁(GIL)。Python对象推荐使用dynamic类型以简化语法。
// 调用无参方法
dynamic test = Py.Import("test");
test.hello();
// 调用有参并获取返回值
dynamic result = test.add(1, 2);
Console.WriteLine(result);
对应的Python脚本test.py需位于PythonEngine.PythonPath指定的目录下:
def hello():
print("hello")
def add(a, b):
return a + b

第3步:集成Anaconda虚拟环境与第三方库
利用Anaconda构建专用虚拟环境可解决复杂的依赖问题:
- 创建环境:
conda create --prefix=F:\\condaenv\\env_name python=3.7 - 激活环境:
conda activate F:\\condaenv\\env_name - 安装库:
pip install numpy
在C#中直接调用NumPy示例:
using (Py.GIL())
{
dynamic np = Py.Import("numpy");
// 注意:数学运算时Python变量需在左侧,如np.pi * 2,而非 2 * np.pi
Console.WriteLine(np.cos(np.pi * 2));
dynamic sin = np.sin;
Console.WriteLine(sin(5));
double c = (double)(np.cos(5) + sin(5));
// 数组操作
dynamic a = np.array(new List<float> { 1, 2, 3 });
dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
Console.WriteLine(a * b);
}
若未正确绑定或环境缺失,将无法导入模块。请确认PythonPath准确指向虚拟环境的Lib/site-packages目录。

第4步:C#对象与Python双向传递
通过ToPython()方法可将C#对象传入Python。需先在C#中定义类结构:
public class Person {
public string FirstName { get; set; }
public string LastName { get; set; }
}
在调用时将实例转换为Python对象:
Person p = new Person("John", "Smith");
dynamic pyPerson = p.ToPython();
string fullName = test.FullName(pyPerson); // 调用test.py中的FullName方法
Python侧接收后即可访问属性:
def FullName(p):
return p.FirstName + " " + p.LastName

第5步:加密与性能优化(PyD文件)
将Python源码编译为pyd(Windows下的C扩展模块)可实现代码保护并提升执行效率。步骤如下:
- 安装编译工具:
pip install cython - 创建
setup.py:from distutils.core import setup from Cython.Build import cythonize setup(name="testName", ext_modules=cythonize("test.py")) - 执行编译:
python setup.py build_ext --inplace
test.cpXX-win_amd64.pyd,可直接重命名为test.pyd供pythonnet调用,效果等同于正常导入但不可逆向查看源码。
执行速度对比分析
通过对比耗时循环任务,验证不同调用方式的性能差异:
- 原生Python执行:
test.py独立运行。 - C#调用Python:通过
pythonnet托管入口调用。 - C#调用PyD:通过编译后的
pyd文件调用。
测试结果参考图示:


数据显示,使用pythonnet会引入少量的跨语言调用开销,但在常规业务逻辑中影响可控;而调用pyd文件由于减少了部分解析层,执行速度甚至优于直接调用纯Python脚本,兼顾了安全性与性能。
以上就是C#/.NET程序调用Python的教程分享的详细内容,更多关于C#调用Python的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













