65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (17投票s)

2006年5月23日

BSD

4分钟阅读

viewsIcon

112669

downloadIcon

2705

用于将 Python 解释器嵌入标准 C++ 代码的包装类

引言

你遇到了一个问题。
你用 C++ 编写了一个跨平台的邮件服务器。
它可以在 Windows、Linux 和 Mac 上运行。

嗯,这有什么问题吗?
不,这很棒。

但现在,你觉得自己很失败。
你的经理说:“一切都很好,但我想让用户通过 GUI 编辑配置文件。”

是的,只需要一个对话框,包含几个输入字段。
但是如何将其添加到你的命令行应用程序中呢?

“Qt?”

“不,我们不能花大价钱只为了一个窗口。”

“Gtk?还是 wxWidgets?”

“不,我们不能给可执行文件增加那么多冗余。”
“而且请记住,我需要尽快完成,最多一天。”

现在你感到进退两难。
这让你心烦意乱,焦头烂额。

我告诉你,保持冷静——继续阅读。

嵌入 Python

是的,这就是你困境的答案,嵌入 Python。
Python 自带 Tkinter——一个轻量级、成熟、跨平台的 UI 工具包。

你可以在几分钟内手工制作你的 UI。而且它不会增加你应用程序的大小。

我会告诉你这有多容易。

“但在此之前,请注意一点:为什么是 Python?”
嗯,以下是我个人喜欢它的原因:

  1. 它很简单。你只需要花几分钟玩一下语言,就可以编写有用的东西。
  2. 语法迫使懒惰的程序员编写清晰、缩进良好的代码。但它不会强制你使用特定的编程风格。
  3. 虽然是解释型的,但性能还是值得称赞的。
  4. 你可以扩展和嵌入它。

许多聪明的人都在要求严苛的系统中使用它。一些最著名的 Python 用户包括:

  1. Google
  2. NASA
  3. 工业光魔 (是的,星球大战很大程度上要归功于 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.cpppyembed.h 添加到你的项目中。

尝试使用示例代码。在应用程序运行时修改 "adduserform.py",看看修改是否能立即生效。是的,你可以修改 UI,而无需重新编译程序。你可以即时修改它。这只是让你的应用程序拥有 Python 界面的众多优势之一。

py_embed 库尚未完成。但它仍然可以用于许多有用的目的。请发送你的评论、建议和错误报告。感谢你一直停留到最后。再见。

历史

  • 2006年5月23日:初始发布
© . All rights reserved.