# G050_PythonCSharp **Repository Path**: LPBStudio/G050_PythonCSharp ## Basic Information - **Project Name**: G050_PythonCSharp - **Description**: Python与C#程序的交互编程 - **Primary Language**: C# - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-10-23 - **Last Updated**: 2026-01-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # G050_PythonCSharp #### 介绍 Python与C#程序的交互编程 #### 软件架构 软件架构说明 ## 方案1: Python.NET - 原理:C# 直接调用Python解释器/库 - 优点:数据传递无额外开销,同步调用高效 - 缺点:依赖 Python 环境,跨平台兼容性一般 - 场景:小数据量、同步 AI 推理(如 TensorFlow 模型调用) #### Python.NET(最推荐,同步高效调用) 核心逻辑:通过Python.NET库,让 C# 直接加载 Python 解释器,调用 Python 函数 / 库,数据(如数组、字典)可直接转换,无需额外序列化。 - 步骤 1:安装依赖 Python 端:确保安装目标库(如numpy、tensorflow) C# 端:NuGet 安装Python.NET(包名:Python.Runtime) - 步骤 2:C# 调用 Python 示例(AI 模型推理场景) 假设 Python 有一个图像分类模型函数predict.py: ~~~ python import numpy as np from tensorflow.keras.models import load_model # 加载模型(全局加载,避免重复初始化) model = load_model("image_classifier.h5") def image_classify(image_data): """ 输入:numpy数组(图像数据,shape=(1, 224, 224, 3)) 输出:int(分类结果) """ result = model.predict(image_data) return np.argmax(result).item() # 转为Python基础类型,方便C#接收 ~~~ - C# 代码调用该函数: ~~~ csharp using System; using Python.Runtime; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; class PythonAICaller { static void Main() { // 1. 初始化Python环境(指定Python安装路径,避免环境变量问题) Runtime.PythonDLL = @"C:\Python39\python39.dll"; // Python的dll路径(Linux/macOS为libpython3.9.so) PythonEngine.Initialize(); try { // 2. 导入Python模块和函数 using (Py.GIL()) // 持有Python全局解释器锁(必须) { // 导入predict.py模块 dynamic predictModule = Py.Import("predict"); // 3. 准备数据(C#图像→numpy数组,直接传递) Bitmap image = new Bitmap(@"test.jpg"); // C#读取图像 float[, , ,] imageArray = BitmapToNumpy(image); // 转换为4维数组(1,224,224,3) // 4. 调用Python函数(数据直接转换,无额外序列化) dynamic result = predictModule.image_classify(imageArray); // 5. 处理结果(Python返回的int直接转为C# int) Console.WriteLine($"分类结果:{result}"); } } finally { // 6. 释放Python环境 PythonEngine.Shutdown(); } } // 辅助函数:C# Bitmap→numpy数组(匹配Python模型输入) static float[, , ,] BitmapToNumpy(Bitmap bmp) { // 缩放图像到224x224(模型输入尺寸) Bitmap resized = new Bitmap(bmp, new Size(224, 224)); float[, , ,] result = new float[1, 224, 224, 3]; // 锁定像素数据,直接读取RGB值 BitmapData bmpData = resized.LockBits( new Rectangle(0, 0, resized.Width, resized.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); int stride = bmpData.Stride; IntPtr ptr = bmpData.Scan0; byte[] bytes = new byte[stride * resized.Height]; Marshal.Copy(ptr, bytes, 0, bytes.Length); // 转换为float(0-1范围,模型常见输入要求) for (int y = 0; y < 224; y++) { for (int x = 0; x < 224; x++) { int idx = y * stride + x * 3; result[0, y, x, 0] = bytes[idx + 2] / 255.0f; // R通道 result[0, y, x, 1] = bytes[idx + 1] / 255.0f; // G通道 result[0, y, x, 2] = bytes[idx] / 255.0f; // B通道 } } resized.UnlockBits(bmpData); return result; } } ~~~ ## 方案2: REST APIPython.NET - 原理:Python 搭服务(FastAPI/Flask),C# 调用 - 优点:跨平台、易扩展,无需耦合进程 - 缺点:网络开销大,小数据量有性能损耗 - 场景:分布式场景、跨机器协同 #### REST API(跨机器 / 分布式场景) 核心逻辑:Python 用FastAPI搭建轻量 API 服务,C# 用HttpClient调用,数据用 JSON/Protobuf 传输。 - 步骤 1:Python 端(FastAPI 服务) ~~~python from fastapi import FastAPI from pydantic import BaseModel import numpy as np from tensorflow.keras.models import load_model app = FastAPI() model = load_model("image_classifier.h5") # 加载模型 # 定义请求体结构(匹配C#传递的数据) class ImageRequest(BaseModel): image_data: list[float] width: int height: int # 定义响应结构 class ImageResponse(BaseModel): class_id: int confidence: float @app.post("/classify", response_model=ImageResponse) async def classify(request: ImageRequest): # 1. 解析数据(list→numpy数组) image = np.array(request.image_data).reshape(1, request.height, request.width, 3) # 2. AI推理 result = model.predict(image) class_id = np.argmax(result).item() confidence = float(np.max(result)) # 3. 返回结果 return {"class_id": class_id, "confidence": confidence} # 启动服务:uvicorn main:app --host 0.0.0.0 --port 8000 ~~~ - 步骤 2:C# 端(调用 API) ~~~ csharp using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; class ApiCaller { // 定义与Python API对应的请求/响应类 public class ImageRequest { public float[] ImageData { get; set; } public int Width { get; set; } public int Height { get; set; } } public class ImageResponse { public int ClassId { get; set; } public float Confidence { get; set; } } static async Task Main() { using (HttpClient client = new HttpClient()) { // 1. 准备数据(C#图像→扁平化数组) float[] imageData = new float[224*224*3]; // 模拟图像数据 var request = new ImageRequest { ImageData = imageData, Width = 224, Height = 224 }; // 2. 序列化JSON并发送POST请求 string json = JsonConvert.SerializeObject(request); StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync("http://localhost:8000/classify", content); // 3. 解析响应结果 if (response.IsSuccessStatusCode) { string responseJson = await response.Content.ReadAsStringAsync(); ImageResponse result = JsonConvert.DeserializeObject(responseJson); Console.WriteLine($"分类结果:{result.ClassId},置信度:{result.Confidence}"); } } } } ~~~ ## 高效协同的关键优化点 #### 数据序列化选择: - 小数据量:JSON(简单);中大数据量:Protobuf(二进制,比 JSON 小 30%+,解析快);超大数据量:直接传递二进制流(如图像字节数组)。 #### 模型 / 资源复用: - Python 端避免每次调用都初始化模型(如方案 1 中全局加载model,方案 2 中 FastAPI 启动时加载),减少重复开销。 #### 避免频繁进程启动: - 不要每次调用都启动 Python 进程(如Process.Start("python.exe")),而是保持 Python 进程常驻(如方案 1 的解释器常驻、方案 2 的 API 服务常驻)。 #### 并行处理: - 多请求场景下,Python 端用FastAPI的异步特性、C# 端用HttpClient的并发请求(HttpClientFactory),提升吞吐量。 ## 方案选择建议 - 同步小数据量(如单张图像推理):选Python.NET,效率最高,代码耦合度低。 - 跨机器/分布式(如多客户端调用):选FastAPI+REST API,易扩展,无需考虑进程耦合。 ## 特技 #### Git错误non-fast-forward的解决方法 当要push代码到git时,出现提示: ~~~ $ git push origin master To ../remote/ ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to '../remote/' ~~~ 产生原因: 别人上传到远程仓库后,你没有及时的同步(、拉取)到本地,但是你同时又添加了一些内容(提交),以致于你在提交时,它会检测到你之前从远程仓库拉取的时候的仓库状态和现在的不一样。于是,它为了安全起见拒绝了你的提交(然后就报了这个错误)。 再者我们可以简单来理解这个问题:我们从字面上理解“non-fast-forward”,可以认为是“不能快速前进”,我觉得有个广告说得好:车到山前必有路……但是路有好走的路,也有不好走的路;而遇到不好走的路时(比如前方遇到拦路石,或者是前方出现岔路),我们就不得不停下来思考“以后的路该怎么走”了,我们“不仅要低头赶路,也要抬头看路”就是这个意思。 “不能快速前进”的原因是因为路不一样了,变得不好走了;体现在git里面就是提交历史出现分叉,主线不再是一条直线,而是在前端出现了分叉,git不知道该如何前进,所以报错了,让你来觉得走哪条路! 解决办法: 1、先把git的东西fetch到你本地然后merge后再push ``` $ git fetch origin master $ git rebase FETCH_HEAD ``` 回到VS系统,对比修改并接受新的修改后。 或者直接执行将修改后的文件增加进去 ~~~ $ git add README.md ~~~ 然后执行: ~~~ $ git rebase --continue ~~~ 进入VIM编辑: ~~~ :q! ~~~