尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

易语言调用Python3全攻略:管道、HTTP与嵌入式方案详解

易语言调用Python3全攻略:管道、HTTP与嵌入式方案详解 简介面向易语言开发者这份压缩包提供了一套易语言与Python3无缝调用的完整方案适合需要在现有易语言项目中嵌入爬虫、数据处理或AI功能的场景。包内包含易语言扩展模块与示例工程配合Python服务端脚本和依赖库实现了从易语言发起调用、传递参数到接收返回结果的闭环省去自行设计通信协议的麻烦。资源共12个文件以e源码、ec模块、py脚本及说明文档为主压缩包大小约51MB并附有运行截图和版本管理配置。目前已有62人学习下载。借助精易模块等辅助组件开发者可以更快搭建跨语言桥接同时通过README和示例代码理解调用流程适合有基础易语言知识、希望扩展Python能力的用户参考。1. 易语言调 Python3先想清楚“无缝”到底指什么做易语言开发的人迟早会遇到一个坎界面、控件、内存操作、大漠插件这类活易语言是真顺手但一旦碰到 JSON 解析、正则批量处理、图像识别、机器学习或者想把某个 Python 写的算法库直接用上易语言自带的库就捉襟见肘了。很多人的第一反应是“我这有 Python 的 exe直接运行它不就行了”但真正做过一两次就知道传参、取返回值、处理中文、等待进程结束每一步都有坑。这个标题里“无缝调用”四个字说的不是把 Python 打包成 exe 再甩给易语言而是让易语言在运行时直接拉起 Python3 解释器、传数据进去、拿结果回来整个过程像调用一个 DLL 一样顺。这篇文就把易语言和 Python3 通信的几种常见做法讲透从最简单的管道通信到嵌入式 C API覆盖不同需求场景最后落在一套易语言模块封装技巧上。2. 先选通信方案管道、HTTP 还是嵌入式判断标准只有一个易语言和 Python3 通信本质上是两个独立进程之间的数据交换。所谓“无缝”是指易语言进程内不需要关心 Python 解释器怎么启动、脚本在哪、依赖库怎么装只关心我传进去什么、拿回来什么。基于这个定位方案选型就清晰了进程越独立稳定性越高但实时性和交互性越差进程越融合效率越高但崩溃风险也越大。常见做法有三种命令行加标准输入输出管道、本地 HTTP JSON 服务、嵌入式 Python C API。我的建议是先按使用场景选而不是按技术难度选。场景 1低频调用每天几十次对延迟不敏感传参简单。比如易语言定时把一批数据丢给 Python 处理完事取回结果。这种用管道方案最合适零依赖、Python 侧就是普通脚本易语言侧一条命令行就能干完。场景 2高频调用一个业务周期内反复请求或者易语言和 Python 需要维持会话状态。比如 Python 侧加载了一个大模型或者训练好的分类器不能每次请求都重新加载。这种必须用常驻进程方案最常见的就是本地 HTTP JSON 服务Python 侧起 Flask 或 http.server易语言侧用客户组件发 POST 请求。场景 3易语言程序要发布给终端用户用户机器上不能保证装了 Python3。这时候就得走嵌入式路线把 Python 解释器以 DLL 形式静态链接进易语言程序或者用 PyInstaller 把 Python 环境连同依赖一起打包成独立目录易语言通过调用 DLL 的方式操作 Python 解释器。这条路坑最多放到第 4 章单独讲。选型列表看起来很长但判断标准就一条你能不能接受 Python 侧是独立进程。能接受优先管道和 HTTP不能接受再上嵌入式。别一上来就搞 C API易语言里操作 PyObject 指针调试成本成倍增加。3. 易语言与 Python3 管道通信最小可运行示例管道通信是入门最快、踩坑最少的方案。原理很简单易语言启动 Python3 解释器执行一个 .py 脚本Python 脚本从 stdin 读取参数把处理结果写到 stdout易语言等进程结束后读取输出。这里有个细节必须先说清楚易语言默认的文本编码是 ANSIGBKPython3 默认字符串是 Unicode在管道传输时编码不一致会导致中文乱码这个在第 5 章专门讲这一章先跑通英文字符串。3.1 易语言侧“运行”命令加重定向易语言自带“运行”命令但它只负责启动程序不负责捕获输出。要拿回 Python 的输出需要把 Python 的标准输出重定向到一个临时文件然后易语言读取这个文件。这看起来不够优雅但它是没有第三方模块时最稳的做法。核心代码逻辑如下.版本 2 .支持库 spec .子程序 调用Python脚本, 文本型, 公开 .参数 脚本路径, 文本型 .参数 输入数据, 文本型, 可空 .局部变量 命令行, 文本型 .局部变量 输出文件, 文本型 .局部变量 临时目录, 文本型 .局部变量 结果文本, 文本型 临时目录 取运行目录 () “\temp” 输出文件 临时目录 “\py_out.txt” 把输入数据写入临时文件避免命令行传参长度和特殊字符问题 写到文件 (临时目录 “\py_in.txt”, 到字节集 (输入数据)) 命令行 “python3 ” 脚本路径 “ ” 临时目录 “\py_in.txt” “ ” 输出文件 运行 (命令行, 假, 1) 等待进程结束 判断循环首 (文件是否存在 (输出文件) 假) 处理事件 () 判断循环尾 结果文本 到文本 (读入文件 (输出文件)) 返回 (结果文本)这段代码里有三个关键参数。第一运行 (命令行, 假, 1)的第三个参数是“等待运行完毕”标志设为 1 表示让易语言程序暂停往下执行直到命令行里的进程退出。第二重定向符号和是操作系统 shell 功能不是 Python 的功能所以命令行必须以字符串形式传给“运行”命令不能拆开。第三判断循环里的处理事件 ()是易语言特有的作用是让窗口消息循环不卡死否则界面会假死。这个方案的好处是输入输出都是文件内容再多也不会撑爆命令行长度限制。3.2 Python 侧从 stdin 读、向 stdout 写Python 侧脚本要写得健壮核心用sys.stdin.read()一次性读取全部输入而不是用input()逐行读。因为易语言写文件时是一次性写完但input()遇到多行文本会只取第一行。处理完的数据用print()输出到 stdout重定向后自然进入输出文件。一个标准模板脚本import sys import json def handle(data): # 这里写你的业务逻辑 try: obj json.loads(data) return {status: ok, result: obj.get(value, 0) * 2} except Exception as e: return {status: error, message: str(e)} if __name__ __main__: raw sys.stdin.read() result handle(raw) print(json.dumps(result, ensure_asciiFalse))这里的逻辑说明stdin.read()会读到 EOF 才返回而文件重定向天然提供了 EOF所以易语言写完文件后重定向启动 PythonPython 能准确拿到完整内容。print()默认在输出末尾加换行这没问题易语言读文件时会完整读入。有两个参数需要根据场景调整json.dumps里的ensure_asciiFalse是为了让中文以原始字符输出而不是 \uXXXX 转义序列如果没有 JSON 依赖直接用print(raw.upper())之类处理也行模板里的 JSON 只是为了演示结构化数据交互。3.3 管道方案的三个必调参数管道方案跑通容易但要稳定运行有三个参数必须调。第一个是python3命令路径。很多 Windows 机器只有python没有python3或者装的是微软商店版命令行里能走通但易语言里拉不起来。常见做法是易语言里加一个“Python路径”配置项默认尝试python3失败自动切换python再不行就让用户手动填解释器绝对路径。第二个是临时文件的写入等待。写完输入文件后立刻重定向运行理论上没问题但杀毒软件或磁盘缓存可能导致 Python 读到的文件不完整。稳妥起见写完文件后加一句文件是否存在的确认或者直接删除旧输出文件之后再运行避免读到上一次的结果。第三个是进程执行时间。Python 脚本如果跑得久易语言的“等待运行”会把界面卡住。处理办法要么在等待循环里加超时计数要么把易语言这边的逻辑改成异步启动 Python 后不等待用一个定时器周期性检查输出文件是否生成生成后再读取。这两种方式分别适合同步交互和后台任务我一般做内部工具用同步做面向用户的界面用异步定时器。配置项示例 [Python] 解释器路径 C:\Python311\python.exe 超时时间(秒) 30 临时目录 .\temp这个配置文件的解析很简单易语言读 INI 用“读配置项”命令。重点解释“解释器路径”参数不要留空让系统去 PATH 里找因为易语言程序发布后运行环境不可控用户机器的 PATH 可能被各种软件改得乱七八糟。指定绝对路径配合一个“自动探测”按钮启动时检测一次路径是否有效能省掉后续大量排障时间。4. 用 HTTP JSON 让易语言和 Python3 保持长连接通信管道方案每次调用都要启动一次 Python 解释器启动耗时大约 200~500 毫秒如果 Python 侧还要加载第三方库比如numpy、pandas、astropy那启动时间可能超过 2 秒。更麻烦的是有些库初始化一次之后才能用重复初始化纯属浪费。这时候就该上常驻进程方案了。我一般推荐用 HTTP JSON因为 HTTP 是事实标准易语言客户组件直接支持 POST 和 GETPython 侧用标准库http.server就能写完全不需要装 Flask契合“离线安装第三方库”的痛点。4.1 易语言客户组件发 POST 请求最低依赖实现易语言的“客户”支持库可能在部分精简版环境里没有所以先检查支持库配置。如果支持库齐全用“客户1.连接”加“客户1.发送数据”就能完成请求。但如果只发送数据不接收响应那实际是单向通信不满足“通信”要求。正确做法是配合“数据到达”事件来异步收响应或者更简单一点直接用 WinHTTP 组件通过命令调用发送 HTTP 请求并同步等待响应。后者在逻辑上更接近其他语言里的requests.post体验。.版本 2 .支持库 spec .支持库 eHttp .子程序 _发送JSON请求, 文本型 .参数 请求地址, 文本型 .参数 JSON数据, 文本型 .局部变量 WinHttp对象, 对象 .局部变量 响应文本, 文本型 WinHttp对象.创建 (“WinHttp.WinHttpRequest.5.1”, ) WinHttp对象.方法 (“Open”, “POST”, 请求地址, 假) WinHttp对象.方法 (“SetRequestHeader”, “Content-Type”, “application/json; charsetutf-8”) WinHttp对象.方法 (“Send”, JSON数据) 等待响应 WinHttp对象.方法 (“WaitForResponse”, 10) 响应文本 WinHttp对象.读文本属性 (“ResponseText”, ) 返回 (响应文本)这个代码里Open的第四个参数设为假表示同步请求执行到Send后会阻塞直到响应返回或超时。WaitForResponse的参数 10 表示最长等待 10 秒。ResponseText拿到的就是 Python 服务端返回的 JSON 字符串。这里有一个对易语言开发者来说特别容易忽略的点JSON 数据里不能有易语言文本型的 Unicode 字节必须先把文本转换成 UTF-8 编码再发送。易语言里用“编码转换”支持库里的“Ansi到Utf8”或“编码_URL编码”处理否则中文到达 Python 侧会变成乱码。4.2 Python 侧常驻服务标准库 http.server 实现Python 侧的服务端有很多人上来就装 Flask但对于这种内部通信场景标准库足够还省去装依赖和打包体积。重写BaseHTTPRequestHandler的核心逻辑用do_POST处理 JSON 请求响应写回 JSONfrom http.server import HTTPServer, BaseHTTPRequestHandler import json import urllib.parse class Handler(BaseHTTPRequestHandler): def do_POST(self): # 读取请求体 length int(self.headers.get(Content-Length, 0)) body self.rfile.read(length).decode(utf-8) try: req json.loads(body) result self.process(req) resp {code: 0, data: result} except Exception as e: resp {code: 1, message: str(e)} # 返回 JSON 响应 data json.dumps(resp, ensure_asciiFalse).encode(utf-8) self.send_response(200) self.send_header(Content-Type, application/json; charsetutf-8) self.send_header(Content-Length, str(len(data))) self.end_headers() self.wfile.write(data) def process(self, req): # 业务逻辑按请求里的 action 分发 action req.get(action, ) if action calculate: return req.get(a, 0) req.get(b, 0) elif action analyze: # 这里可以加载一次模型保存在全局 return run_analysis(req.get(text, )) return {error: unknown action} def log_message(self, fmt, *args): # 静默访问日志避免控制台刷屏可改为写日志文件 pass model None def run_analysis(text): global model if model is None: # 模拟加载大模型 model {loaded: True} return {model_loaded: model[loaded], text_length: len(text)} if __name__ __main__: server HTTPServer((127.0.0.1, 8765), Handler) print(server running on 8765) server.serve_forever()这个例子里的process方法做了一件事按请求里的action字段分发给不同函数。这是常用套路因为单进程单端口服务多个功能比每个功能开一个端口好管理。run_analysis里演示了全局变量model的懒加载第一次调用时加载模型后续调用直接复用这就解决了管道方案里反复加载耗时长的问题。4.3 必调参数与踩坑请求体大小、端口占用、线程安全三个参数值得单独说。第一个是Content-Length头易语言发送 POST 请求时一些封装库会自动带上这个头但如果是手动拼报文忘了加Content-LengthPython 侧self.rfile.read(length)读到的长度为 0请求体为空返回的永远是错误。这是 HTTP JSON 通信里最常见的低级错误。第二个是端口占用。HTTPServer默认是单线程一次只处理一个请求如果易语言并发发多个请求后面的会排队。更麻烦的是服务崩溃后端口不会立刻释放重启时报 “Address already in use”。解决办法是对端口做预检测Python 侧启动时先尝试绑定失败就打印提示并等待几秒重试。第三个是ThreadingHTTPServer的选型。Python 3.7 之后有现成的ThreadingHTTPServer继承自socketserver.ThreadingMixIn每个请求一个线程。但注意如果业务逻辑里有共享全局变量比如上面的model多线程并发访问需要加锁。易语言侧如果是单客户组件一般不会触发并发但保险起见服务端加一个threading.Lock包裹模型推理部分。5. 嵌入式调用把 Python3 解释器装进易语言程序里管道和 HTTP 都要求目标机器上存在可用的 Python3 解释器。如果易语言写的是一个要发给客户用的工具客户机器上没有 Python 环境或者系统 Python 版本、路径不可控这两个方案都悬。嵌入式方案的意思是把 Python3 的解释器核心python3.dll连同标准库、第三方依赖库一起放在易语言程序目录下易语言直接通过 DLL 命令调用 Python 的 C API整个程序对外看起来就是一个普通的 exe用户不需要感知 Python 的存在。5.1 环境部署嵌入式 Python 包与依赖库位置嵌入式部署的常规做法是从 Python 官网下载 Windows embeddable package解压后得到 python311.dll、python311.zip内含标准库、一组 DLL 文件。然后需要修改python311._pth文件把标准库和第三方库的路径写正确。易语言程序目录结构通常如下程序目录/ ├── 主程序.exe ├── python311.dll ├── python311.zip ├── python311._pth ├── Lib/site-packages/ # 第三方库安装位置 └── 业务脚本.py_pth文件的内容决定了解释器的模块搜索路径。一个可用的配置是python311.zip Lib/site-packages . import site最后一行import site很关键没有它site-packages里的第三方库不会自动加入sys.path离线安装的numpy等库会全部 import 失败。易语言侧调用 DLL 命令前先设置环境变量PYTHONHOME指向程序目录再Py_Initialize初始化解释器就能保证解释器只在程序目录找模块不受系统其他 Python 影响。5.2 易语言声明 Python C API从初始化到执行单行代码易语言里调用 DLL 命令用的是“DLL命令”定义声明函数名、参数类型、返回值类型。Python C API 的函数参数和返回值大量使用PyObject*指针在易语言里统一声明为“整数型”。初始化流程分三步设置路径、初始化解释器、执行脚本。DLL 声明示例如下.版本 2 .DLL命令 Py_Initialize, , python311.dll, Py_Initialize .参数 无 .DLL命令 Py_Finalize, , python311.dll, Py_Finalize .参数 无 .DLL命令 PyRun_SimpleString, 整数型, python311.dll, PyRun_SimpleString .参数 代码, 文本型 .DLL命令 PyRun_SimpleFile, 整数型, python311.dll, PyRun_SimpleFileExFlags .参数 文件指针, 整数型 .参数 文件名, 文本型 .参数 全局变量, 整数型 .参数 标志, 整数型调用顺序不能乱先Py_Initialize再PyRun_SimpleString程序退出前一定调用Py_Finalize。如果一个易语言程序里初始化了两次解释器第二次会直接崩溃这是 C API 的硬性限制。实际执行脚本时不推荐用PyRun_SimpleFile直接跑 .py 文件因为文件路径和编码问题会把问题复杂化。更可控的方式是先把 Python 代码读成文本再用PyRun_SimpleString执行。但要注意PyRun_SimpleString执行的是单条语句或单个代码块如果脚本里有中文注释且易语言读出文本是 ANSI 编码Python 解释器会报编码错误。所以在把脚本喂给解释器之前必须把文本转为 UTF-8 字节再用 C API 的PyRun_String配合Py_file_input标志执行。5.3 一个完整的嵌入式调用流程结合上面声明易语言子程序的执行流程如下.子程序 _嵌入式执行Python, 文本型, 公开 .参数 脚本内容, 文本型 .局部变量 utf8脚本, 字节集 转成 UTF-8 utf8脚本 编码转换 (到字节集 (脚本内容), #编码_GBK, #编码_UTF_8, ) 写到临时文件再读成 char*或直接用指针方式传入 写到文件 (取运行目录 () “\tmp_script.py”, utf8脚本) 假设已经初始化过解释器直接在易语言里调用 PyRun_SimpleFile 这里需要用 文件流方式 打开文件实际易语言里可配合 打开文件 获取文件号 Py_Run_SimpleFile (文件号, “tmp_script.py”, 0, 0) 读取结果文件 返回 (到文本 (读入文件 (取运行目录 () “\tmp_result.txt”)))这个流程里的参数说明PyRun_SimpleFile的第一个参数是FILE*指针易语言中没有原生指针类型需要通过外部库把易语言的文件号转成 C 文件指针或者用_wfopen打开文件拿到指针。这一步是嵌入式方案里最容易卡住的地方。我的建议是可以跳过PyRun_SimpleFile直接在 Python 脚本内部用open()读取输入文件和写结果文件易语言只负责把文件准备好调用PyRun_SimpleString执行一个短字符串比如exec(open(rC:\path\script.py).read())。这样只需要传一个合法的路径字符串给解释器完全绕开文件指针转换。PyRun_SimpleString执行时的编码问题同样需要注意字符串必须是 UTF-8因为易语言文本型在 DLL 调用边界会按 ANSI 转换为char*不是 UTF-8。解决方法是先转换成字节集取字节集指针作为参数传入。这一步不处理好脚本里任何中文字符串都会导致执行失败或乱码。具体的编码转换技巧在第 6 章统一说明。6. 中文编码与调试技巧封装成易语言模块才是终点易语言和 Python3 通信的所有方案里中文乱码是出现频率最高的疑难杂症。根因只有一个易语言的“文本型”在内存中是 ANSI 编码的字节序列而 Python3 的str是 Unicode系统默认按 UTF-8 编码读写外部数据。两者转换稍微错位轻则显示乱码重则抛出UnicodeDecodeError整个脚本中断。6.1 三种场景的编码处理规格按通信方式分类处理方式不同。管道方案里输入文件和输出文件都是字节流易语言侧写到文件用“到字节集”得到 ANSI 字节Python 侧读取时用open(file, encodinggbk)显式指定编码反过来 Python 写结果文件时用open(file, w, encodinggbk)易语言读入后直接到文本不需要额外转换这样两边都用 GBK全程一致。HTTP JSON 方案里POST 请求体必须 UTF-8Python 侧decode(utf-8)响应也指定charsetutf-8易语言收响应后要把字节集从 UTF-8 转到 ANSI 再转文本。嵌入式方案里传进PyRun_SimpleString的字符串或者脚本文件落地编码统一用 UTF-8。# Python 侧读写文件的通用模板 import sys def read_input(): # 尝试 UTF-8失败回退 GBK兼容两种调用方 raw sys.stdin.buffer.read() for enc in (utf-8, gbk): try: return raw.decode(enc) except UnicodeDecodeError: continue return raw.decode(utf-8, errorsignore)这个模板里的sys.stdin.buffer.read()拿到的是原始字节不经过解码然后用循环尝试不同编码。这样不管易语言侧传过来的是 UTF-8 还是 GBKPython 都不至于直接崩。对应的输出侧也优先输出 UTF-8并通过 HTTP 头或管道文件名的约定让易语言那边知道用哪个编码读取。6.2 找错方法让 Python 把异常写到文件而不是控制台管道方案里Python 脚本一抛异常traceback 会写到 stderr但易语言重定向时经常只重定向了 stdout没管 stderr。结果就是 Python 崩了易语言读回来的输出文件是空的排查无从下手。我的习惯是在所有 Python 脚本开头加一个全局异常捕获把异常堆栈写入一个独立的错误日志文件import sys import traceback def main(): # 业务逻辑入口 pass if __name__ __main__: try: main() except Exception: with open(error.log, w, encodingutf-8) as f: traceback.print_exc(filef) sys.exit(1)这个写法的参数说明traceback.print_exc(filef)把完整堆栈打印到文件对象比直接打印到 stderr 可控。易语言侧在“等待运行”结束后先检查error.log是否存在存在就把内容读回来显示给用户这样哪怕 Python 脚本因语法错误无法启动也能在易语言界面上看到具体错误行号。另一个常用的调试技巧是在易语言调用命令行的最前面加cmd /c这样重定向符号由 cmd 处理兼容性更好但加了cmd /c之后错误信息会混在输出里所以分开操作更稳妥干脆让 Python 自己管理日志文件。6.3 进阶封装把整套通信模式做成易语言模块到这里底层方案已经齐了最后一步是把这些逻辑封装成一个易语言模块对外只暴露一个易用的子程序。我经常在模块里设计两个公开接口一个同步调用一个异步调用。同步调用的签名是“调用Python (脚本文件名作为标识, 输入数据, 超时秒数) → 返回文本”内部根据配置自动选择管道、HTTP 或嵌入式通道异步调用则是把同样的参数交给一个线程完成后通过回调事件通知界面更新。这样业务代码里完全不出现任何通信细节后续想换通信方式只需要改模块内部的“选择通道”逻辑业务子程序一行不用动。封装时有一个必须考虑的细节不同通信通道对输入参数的类型要求不同管道要求的是文本HTTP 要求的是 JSON 序列化后的文本嵌入式要求在内存中构造 Python 对象。为了统一模块内部强制约定所有进出的数据都用 JSON 字符串表达。管道方案里易语言先把 JSON 字符串写入输入文件HTTP 方案里直接作为 POST body嵌入式方案里先构造 Python 字典再转 JSON。这样易语言侧只需要处理 JSON 的序列化和反序列化易语言自带“json”支持库只支持部分语法如果觉得限制太多可以用第三方 json 模块。这种统一封装的收益是长远的等到 Python 侧升级接口、或者从管道切到 HTTP 时易语言主程序完全不受影响。本文还有配套的精品资源点击获取
返回列表