将 Python 嵌入到您的 C++ 应用程序中






4.58/5 (17投票s)
用于将 Python 解释器嵌入标准 C++ 代码的包装类
引言
你遇到了一个问题。
你用 C++ 编写了一个跨平台的邮件服务器。
它可以在 Windows、Linux 和 Mac 上运行。
嗯,这有什么问题吗?
不,这很棒。
但现在,你觉得自己很失败。
你的经理说:“一切都很好,但我想让用户通过 GUI 编辑配置文件。”
是的,只需要一个对话框,包含几个输入字段。
但是如何将其添加到你的命令行应用程序中呢?
“Qt?”
“不,我们不能花大价钱只为了一个窗口。”
“Gtk?还是 wxWidgets?”
“不,我们不能给可执行文件增加那么多冗余。”
“而且请记住,我需要尽快完成,最多一天。”
现在你感到进退两难。
这让你心烦意乱,焦头烂额。
我告诉你,保持冷静——继续阅读。
嵌入 Python
是的,这就是你困境的答案,嵌入 Python。
Python 自带 Tkinter——一个轻量级、成熟、跨平台的 UI 工具包。
你可以在几分钟内手工制作你的 UI。而且它不会增加你应用程序的大小。
我会告诉你这有多容易。
“但在此之前,请注意一点:为什么是 Python?”
嗯,以下是我个人喜欢它的原因:
- 它很简单。你只需要花几分钟玩一下语言,就可以编写有用的东西。
- 语法迫使懒惰的程序员编写清晰、缩进良好的代码。但它不会强制你使用特定的编程风格。
- 虽然是解释型的,但性能还是值得称赞的。
- 你可以扩展和嵌入它。
许多聪明的人都在要求严苛的系统中使用它。一些最著名的 Python 用户包括:
- NASA
- 工业光魔 (是的,星球大战很大程度上要归功于 Python!)
“好吧,好吧。我承认,Python 本身是一种很好的语言。”
“但我受够了尝试嵌入它。”
“C API 非常底层,而且它从来没有按文档说明的那样工作。”
是的,我同意关于嵌入 Python 的文档有些不足。但请记住我说的,“我会给你展示一种简单的方法”。
我编写了一个 Python API 的 C++ 包装器。直接使用它即可。
你不再需要担心将 Python 对象映射到 C/C++ 类型。你不需要为垃圾回收而在堆上进行引用计数。
一切都已处理妥当。直接使用即可。
Using the Code
我们将要使用的类声明在 'pyembed.h' 中。
考虑以下 Python 脚本:
列表 1 - test.py
def multiply(a,b):
"Finds a product, the other way round!"
c = 0
for i in range(0, a):
c = c + b
return c
函数 "multiply
" 接收两个整数作为参数并返回一个整数。让我们编写一些代码从 C++ 中调用这个函数。
列表 2 - test_pyembed.cpp
#include <iostream>
#include "pyembed.h"
using namespace pyembed; // for brevity
int
main(int argc, char** argv)
{
try // always check for Python_exceptions
{
// Create an instance of the interpreter.
Python py(argc, argv);
// Load the test module into the interpreter
py.load("test");
// value returned by python is stored here
long ret = 0;
// our argument list that maps string values
// to their python types
Arg_map args;
// fill up argument strings and their types.
args["10"] = Py_long;
args["20"] = Py_long;
// make the call
py.call("multiply", args, ret);
// will print 200
std::cout << ret << '\n';
return 0;
}
catch (Python_exception ex)
{
std::cout << ex.what();
}
return 0;
}
我不会解释这段代码,因为它注释得很清楚。我们这里的主要参与者是 "Python
" 类。它有多种 "call
" 函数,可用于调用返回不同类型对象的 Python 函数。以下 Python 类型被处理为返回值并映射到 C++ 类型:
Python 返回 | 映射到 |
PyString |
std::string |
PyLong /PyInt |
long |
PyFloat |
double |
PyTuple |
std::vector<std::string> (类型别名为 String_list) |
PyList |
std::vector<std::string> (类型别名为 String_list) |
PyDict |
std::map<std::string, std::string> (类型别名为 String_map) |
嵌入在其他对象中的元组、列表和字典将作为逗号或冒号分隔的字符串返回,你需要进一步解析它们。但这种值很少需要。
你可以将以下 C++ 类型作为参数传递给 Python 函数:
long | (Py_long) |
double |
(Py_real) |
std::string 或 char |
(Py_string) |
你还可以使用 "run_string
" 函数执行任意 Python 脚本。
要执行脚本文件,请使用 "run_file
" 函数。
示例
py.run_string("print 'Hello World from Python!');
py.run_file("test.py");
只需查看 pyembed.h 即可了解我们 "Python
" 类提供的所有功能的概况。
可下载的 zip 文件包含 pyembed 库的代码,以及一个演示 Tkinter GUI 的小型 C++ 命令行程序。
编译
这是我在 Linux 上编译示例应用程序 "users.cpp" 时使用的命令:
g++ -o users -I/usr/local/include/python2.4 pyembed.cpp
users.cpp -L/usr/local/lib/python2.4/config -Xlinker
-export-dynamic -lpython2.4 -lm -lpthread -ltk -lutil
你需要更改 -I
和 -L
选项以指向你的 Python 安装文件夹。
要在 Windows(或其他任何平台)上编译,请下载并安装 Python,然后将应用程序与特定于平台的 Python 库链接。如果你使用的是 Visual C++,你可能还需要与多线程运行时库链接。我不在 Windows 上工作,所以这只是我的猜测。始终记住将 pyembed.cpp 和 pyembed.h 添加到你的项目中。
尝试使用示例代码。在应用程序运行时修改 "adduserform.py",看看修改是否能立即生效。是的,你可以修改 UI,而无需重新编译程序。你可以即时修改它。这只是让你的应用程序拥有 Python 界面的众多优势之一。
py_embed
库尚未完成。但它仍然可以用于许多有用的目的。请发送你的评论、建议和错误报告。感谢你一直停留到最后。再见。
历史
- 2006年5月23日:初始发布