如何使我的 Python 程序提供的功能可用于在同一台或其他计算机上运行的以其他语言编写的程序?

Posted

技术标签:

【中文标题】如何使我的 Python 程序提供的功能可用于在同一台或其他计算机上运行的以其他语言编写的程序?【英文标题】:How can I make the functions provided by my Python program available to programs written in other languages running on the same or other computers? 【发布时间】:2021-09-20 03:16:31 【问题描述】:

我创建了一个用 Python 编写的系统,提供各种硬件控制、数据采集和处理功能。我希望可以从用 Python 或其他语言(如 Lua、Matlab、Octave、C++ 和可能的其他语言)编写的程序访问这个系统。 此外,这些程序可能在该计算机上或通过网络连接的另一台计算机上运行。

我知道存在各种用于 Python 的 RPC 库,例如

https://pypi.org/project/simple-rpc/(不提供与多种语言的轻松接口) Ice https://zeroc.com/products/ice(对于我的简单应用程序来说太大了)

是否有任何简单、轻量级的解决方案可以从其他语言编写的程序中远程调用 Python 函数?

【问题讨论】:

这篇文章不太适合 ***;它非常开放,您似乎对获得解决方案的反馈比解决特定的错误/问题更感兴趣。也许codereview.stackexchange.com 可能是一个更好的论坛? 根据***.blog/2011/07/01/… 的建议,我决定在这里发布它。在我看来,它与该页面中设置的规则一致。 这里的问题不是你在回答你自己的问题(实际上这是鼓励的)。问题是它是一个非问题/软件推荐帖子,而提供的答案实际上是一篇宣传 github 包的博文。我个人对此不以为然,但知道通常此类帖子往往会因为违反 SO 准则而被关闭。顺便说一句,如果您要寻找的是曝光,您可能想查看"Show HN" section on HackerNews。通过这种方式,您将有更好的运气来提高对您的包裹的认识。 解决方案是从解决实际问题的需要演变而来的。如何在资源极少的环境(例如运行 MicroPython 的微控制器)中创建实现某些服务的 Python 代码并从其他语言访问它。例如,基于微处理器的测量系统连接到网络并从 Octave/Matlab 访问以控制它并获取数据。所以也许我应该改写问题以专注于该问题而不是寻找替代方案...... 【参考方案1】:

我在 2012 年开发了一个类似的系统,其中 Python 函数的参数和返回值通过 MessagePack 传递。这样的解决方案能够与用各种语言编写的程序进行通信。它发布在 Usenet alt.sources 组 https://groups.google.com/g/alt.sources/c/QasPxJkKIUs (也可在 funet 存档 http://ftp.funet.fi/pub/archive/alt.sources/2722.gz 中找到)。 现在我用 ZeroMQ 库对其进行了补充,该库提供与运行在同一台机器或远程机器上的程序的有效通信。

系统的核心是用Python编写的服务器:

#!/usr/bin/python3
"""
   The code below implements a simple ZeroMQ and MessagePack RPC server.
   Written by Wojciech M. Zabolotny, wzab<at>ise.pw.edu.pl or wzab01<at>gmail.com
   13.04.2021

   The code is based on a post: "Simple object based RPC for Python",
   https://groups.google.com/g/alt.sources/c/QasPxJkKIUs/m/An1JnLooNCcJ
   https://www.funet.fi/pub/archive/alt.sources/2722.gz

   This code is published as PUBLIC DOMAIN  or under 
   Creative Commons Zero v1.0 Universal license (whichever better suits your needs).
"""
import time
import zmq

import msgpack as mp
import traceback
import os

#Functions which process requests
def remote_mult(a,b):
    return a*b

def remote_div(a,b):
    print(a,b,a/b)
    return a/b

def cat_file(fname):
    f=open(fname,"rb")
    return f.read()

#Table of functions
func=
  'mult':remote_mult,
  'div':remote_div,
  'file':cat_file,



def handle(msg):
    try:
        obj = mp.unpackb(msg)
        if len(obj) != 2:
            raise Exception("Wrong number of RPC objects, should be 2: name and arguments")
        if isinstance(obj[1],tuple) or isinstance(obj[1],list):
            res=func[obj[0]](*obj[1])
        elif isinstance(obj[1],dict):
            res=func[obj[0]](**obj[1])
        else:
            raise Exception("Wrong type of arguments in RPC, should be list, tuple or dictionary")
        res = ("OK", res)
    except Exception:
        res=("error", traceback.format_exc())
    return mp.packb(res)

if __name__ == "__main__":
    context = zmq.Context()
    socket = context.socket(zmq.REP)
    # Create the server, binding to localhost on port 9999
    socket.bind("tcp://*:9999")
    while True:
        msg = socket.recv()
        res = handle(msg)
        socket.send(res)

服务器可能会发布具有不同参数的不同函数。参数可以作为位置参数(然后它们应该在元组中传递)或关键字参数(然后它们应该作为映射/字典传递)给出。

下面给出了使用这些函数的 Python 客户端示例:

#!/usr/bin/python3
"""
   The code below implements a simple ZeroMQ and MessagePack RPC client.
   Written by Wojciech M. Zabolotny, wzab<at>ise.pw.edu.pl or wzab01<at>gmail.com
   13.04.2021

   The code is based on a post: "Simple object based RPC for Python",
   https://groups.google.com/g/alt.sources/c/QasPxJkKIUs/m/An1JnLooNCcJ
   https://www.funet.fi/pub/archive/alt.sources/2722.gz

   This code is published as PUBLIC DOMAIN  or under 
   Creative Commons Zero v1.0 Universal license (whichever better suits your needs).
"""
import socket
import sys
import msgpack as p
import zmq

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])
objects=[
    ["mult",(4,5)],
    ["mult","a":7,"b":8],
    ["div","a":9,"b":4],
    ["file",("/etc/passwd",)],
    ["file",("/etc/non_existing_file",)],
]


context = zmq.Context()

socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:9999")    

for obj in objects:
    socket.send(p.packb(obj))
    #  Get the reply.
    msg = socket.recv()
    resp = p.unpackb(msg)
    print("Received reply", resp)

以下是用其他语言编写的客户端。

在 Lua 中:

-- Demonstrator of the communication with simple Python RPC server from Lua
-- Written by Wojciech M. Zabołotny (wzab01<at>gmail.com or wzab<at>ise.pw.edu.pl)
-- Copyright: This program is released into the public domain or under
-- Creative Commons Zero v1.0 Universal license (whichever better suits your needs).
local zmq = require "lzmq"
--require "utils"
local mp = require "mpack"
--print_version(zmq)
local pr = require "pl.pretty"
context  = assert(zmq.context())
rpcsrv = assert(context:socket (zmq.REQ))
assert(rpcsrv:connect("tcp://localhost:9999"))

function rpc(params)
    local req=mp.pack(test)
    rpcsrv:send(req)
    local rcv=rpcsrv:recv()
    local res=mp.unpack(rcv)
    return res  
end

test = "file","/etc/passwd"
local res = rpc(test)
pr.dump(res)
test = "mult",7,8
res = rpc(test)
pr.dump(res)
test = "div",b=4.0,a=9.0
res = rpc(test)
pr.dump(res)
-- The above works, but 9/4 is printed as 2.
print(res[2])
test = "div",a=4.0,b=9.0
res = rpc(test)
pr.dump(res)
-- The above works, but 4/9 is printed as 0.
print(res[2])

八度音

% Demonstrator of the communication with simple Python RPC server from Octave
% Written by Wojciech M. Zabołotny (wzab01<at>gmail.com or wzab<at>ise.pw.edu.pl)
% Copyright: This program is released into the public domain.
pkg load jsonlab
pkg load zeromq

srv = zmq_socket (ZMQ_REQ);
zmq_connect (srv, "tcp://localhost:9999");


function res = rpc(req,fname,fargs,maxlen=10000)
  x=fname, fargs;
  a=savemsgpack(x);
  zmq_send(req,a);
  w=zmq_recv(req,maxlen);
  y=loadmsgpack(char(w));
  if strcmp(char(y1),"OK")
     res = y2;
  end
  if strcmp(char(y1),"error")
     error(char(y2));
  end
endfunction
  
res = rpc(srv,"mult",struct("a",13,"b",20));
res
res = rpc(srv,"mult",17,3);
res
res = rpc(srv,"file","/etc/passwd");
char(res')

最后是 C++

// Demonstrator of the communication with simple Python RPC server from C++
// Written by Wojciech M. Zabołotny (wzab01<at>gmail.com or wzab<at>ise.pw.edu.pl)
// Copyright: This program is released into the public domain or under
// Creative Commons Zero v1.0 Universal license (whichever better suits your needs).
#include <string>
#include <zmq.hpp>
#include <iostream>
#include <msgpack.hpp>

msgpack::object_handle rpc(zmq::socket_t &sock, auto req)

   std::size_t offset = 0;
   std::stringstream rstr;
   msgpack::pack(rstr,req);
   zmq::message_t msg(rstr.str());
   sock.send(msg,zmq::send_flags::none);
   auto res = sock.recv(msg, zmq::recv_flags::none);
   auto oh = msgpack::unpack((const char *)msg.data(),msg.size(),offset);
   return oh;


int main(void) 
   zmq::context_t ctx;
   zmq::socket_t sock(ctx, zmq::socket_type::req);
   sock.connect("tcp://localhost:9999");
   msgpack::object_handle res;
   res = rpc(sock,std::tuple<std::string, std::array<int,2>>("mult", 7, 8));
   std::cout << res.get() << std::endl;
   res = rpc(sock,std::tuple<std::string, std::map<std::string,int>>("div", "a",8,"b",7));
   std::cout << res.get() << std::endl;
   res = rpc(sock,std::tuple<std::string, std::map<std::string,int>>("div", "b",8,"a",7));
   std::cout << res.get() << std::endl;
   res = rpc(sock,std::tuple<std::string, std::tuple<std::string>>( "file", "/etc/passwd"));
   std::cout << res.get() << std::endl;

上面的 C++ 代码必须使用 -fconcepts-ts 选项编译,例如如下:

c++ obj_rpc_cli.cc -o obj_rpc_cli -fconcepts-ts -g -lzmq -lmsgpackc

解决方案非常简单。维护的版本在 github 存储库中可用:https://gitlab.com/WZab/python-versatile-rpc

【讨论】:

以上是关于如何使我的 Python 程序提供的功能可用于在同一台或其他计算机上运行的以其他语言编写的程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何使我的 Visual c++ 2013 应用程序不需要下载可再发行包?

如何使我的 RelativeLayout 可滚动? [复制]

Python书读千遍不如手敲一行

如何使我的 ListView 在颤动中可滚动?

如何安全地赋予我的功能而不使我的主要形式卡住

我如何使我的PHP应用程序从不要求提供Gmail API验证码?