这两天基于CSDN【码客巅云】转载的【C#调用python脚本样例】进行学习,过程中遇到不少坑,折腾了好久,改了不少代码和配置才避坑成功。想到大家,特别是初学者可能也会遇到这些问题,特分享复盘如下。期望能帮助大家。
文章目录
1.工具环境
环境和工具。
- Visual Studio 2022
- 框架:.NET 6.0
- 依赖NuGet包:ironPython 2.7.11
- 运行环境:Python 3.9 64位
提示:以下是本篇文章正文内容,下面案例可供参考
2.创建C#项目
使用console控制台应用
使用.NET6.0
3.安装ironPython
通过NuGet在线安装【ironPython 2.7.11】。
切记!!!不要安装【预发行版】
最终在依赖项中看到已下载好的ironPython
4.Python代码文件
创建一个英文文件夹【PythonCodeFile】用于存放Python的代码文件
打开Python代码文件所在文件夹,拖动至资源管理器中【PythonCodeFile】文件夹上方,文件会自动复制到项目目录下。
5.创建类
一共要创建两个类:
- 一个是用于存放数据对象的类【DateObjectTest.cs】
- 另一个是用来调用Python代码的执行类【RunPython.cs】
引用都是:
using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
5.1.类【DateObjectTest.cs】代码
代码如下(示例),命名空间大家记得改成自己的:
using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
namespace ConsoleAppRunPythonCode001
{
public class DataObjectTest
{
public DataObjectTest(string userName, int age, string desc)
{
UserName = userName;
Age = age;
Desc = desc;
}
/// <summary>
/// 属性:姓名
/// </summary>
public string UserName { set; get; }
/// <summary>
/// 属性:年龄
/// </summary>
public int Age { set; get; }
/// <summary>
/// 属性:描述
/// </summary>
public string Desc { set; get; }
public void AddAge(int age)
{
this.Age += age;
this.Desc = String.Format("{0}长大了{1}岁(from C#)", this.UserName, age);
}
public override string ToString()
{
return String.Format("姓名:{0}; 年龄:{1}; 描述:{2}", this.UserName, this.Age, this.Desc);
}
}
}
5.2.类【RunPython.cs】代码
代码如下(示例),命名空间大家记得改成自己的:
using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
namespace ConsoleAppRunPythonCode001
{
internal class RunPython
{
public void RunPythonTest()
{
// 加载外部 python 脚本文件.
ScriptRuntime pyRumTime = Python.CreateRuntime();
dynamic obj = pyRumTime.UseFile(@"E:\MyPython\ConsoleAppRunPythonCode001\ConsoleAppRunPythonCode001\PythonCodeFile\TestPythonFile.py");
// ==================================================
// 简单调用脚本文件中的方法.
Console.WriteLine(obj.welcome("Test C# Call Python."));
Console.WriteLine(obj.welcome("测试中文看看是否正常!"));
// ==================================================
// 测试自定义对象.
DataObjectTest testObj = new DataObjectTest("张三", 20, "");
Console.WriteLine("调用脚本前对象数据:{0}", testObj);
obj.testAddAge(testObj);
Console.WriteLine("调用 testAddAge 脚本后,对象数据={0}", testObj);
obj.testAddAge2(testObj);
Console.WriteLine("调用 testAddAge2 脚本后,对象数据={0}", testObj);
// ==================================================
// 测试 List.
IronPython.Runtime.List testList = new IronPython.Runtime.List();
testList.Add("List数据1");
testList.Add("List数据2");
testList.Add("List数据3");
// 测试参数为 List.
string result = obj.testList(testList);
Console.WriteLine("调用 testList , 返回结果:{0}", result);
// ==================================================
// 测试 Set.
IronPython.Runtime.SetCollection testSet = new IronPython.Runtime.SetCollection();
testSet.add("Set数据1");
testSet.add("Set数据2");
testSet.add("Set数据3");
// 测试参数为 Set.
result = obj.testSet(testSet);
Console.WriteLine("调用 testSet , 返回结果:{0}", result);
// ==================================================
// 测试 Dictionary.
IronPython.Runtime.PythonDictionary testDictionary = new IronPython.Runtime.PythonDictionary();
testDictionary["Key1"] = "Value1";
testDictionary["Key2"] = "Value2";
testDictionary["Key3"] = "Value3";
// 测试参数为 Dictionary.
result = obj.testDictionary(testDictionary);
Console.WriteLine("调用 testDictionary , 返回结果:{0}", result);
Console.ReadLine();
}
}
}
【RunPython.cs】代码中需要修改Python文档的目录
复制Python代码文件的完整路径,设置【RunPython.cs】中的目录
5.3.编辑启动类【Program.cs】
记得必须在第一行,引用当前项目的命名空间,否则将无法调用
using ConsoleAppRunPythonCode001;//记得必须在第一行,引用当前项目的命名空间,否则将无法调用。名字请改成自己的。
// See https://aka.ms/new-console-template for more information
//Console.WriteLine("Hello, World!");
Console.WriteLine("开始执行Python脚本");
RunPython RunPython = new RunPython();
RunPython.RunPythonTest();
6.最后运行试试
正确的运行结果
开始执行Python脚本
hello (from Python) Test C# Call Python.
hello Test C# Call Python.
hello (from Python) 测试中文看看是否正常!
hello 测试中文看看是否正常!
调用脚本前对象数据:姓名:张三; 年龄:20; 描述:
张三 又大了一岁,测试是否中文乱码(from Python)
调用 testAddAge 脚本后,对象数据=姓名:张三; 年龄:21; 描述:张三 又大了一岁,测试是否中文乱码(from Python)
调用 testAddAge2 脚本后,对象数据=姓名:张三; 年龄:23; 描述:张三长大了2岁(from C#)
调用 testList , 返回结果: List数据1 List数据2 List数据3
调用 testSet , 返回结果: Set数据2 Set数据1 Set数据3
调用 testDictionary , 返回结果: Key1=Value1; Key3=Value3; Key2=Value2;
7.问题报错处理
7.1.异常object has no attribute
如果出现【System.MissingMemberException:“‘DataObjectTest’ object has no attribute ‘Age’”】,那是因为存放数据对象的类【DateObjectTest.cs】使用了默认的【internal】而不能被外部Python程序访问(即不能访问目标类)。需要修改访问修饰符为【public】。
类【DateObjectTest.cs】中修改
7.2.来自Python的输出出现中文乱码
请参考我的另一篇文章【C#调用Python中文乱码问题处理】
C#调用Python3中文乱码问题处理(在Visual Studio 2022 中基于.NET 6.0框架调用Python3.9代码)
8.Python代码
# -*- coding: utf-8 -*-
# 第一行的目的,是为了让代码里面,可以有中文信息. (will否则要运行报错)
# 这个 Python 脚本, 用于被 C# 来调用.
def welcome(name):
print "hello (from Python) " + name
return "hello " + name
# 测试 参数为 C# 对象的效果. (获取/设置 C# 对象的属性)
def testAddAge(obj):
obj.Age = obj.Age + 1
obj.Desc = obj.UserName + " 又大了一岁,测试是否中文乱码(from Python)"
print obj.Desc
# 测试 参数为 C# 对象的效果. (调用 C# 对象的方法)
def testAddAge2(obj):
obj.AddAge(2)
# 测试 List.
def testList(lst):
vResult = ""
for each_item in lst:
vResult = vResult + " " + each_item
return vResult
# 测试 Set.
def testSet(pSet):
vResult = ""
for each_item in pSet:
vResult = vResult + " " + each_item
return vResult
# 测试 Dictionary
def testDictionary(pDictionary):
vResult = ""
for each_item in pDictionary:
vResult = vResult + " " + each_item + "=" + pDictionary[each_item] + ";"
return vResult
最后可参考
C#调用Python3中文乱码问题处理(在Visual Studio 2022 中基于.NET 6.0框架调用Python3.9代码)