Boost(8):Boost.Python 实现 Python 对象以及各种类型的内置操作和方法

说明

Python 有多种非常好用的数据类型,如 Numbers,String,List,Tuple,Dictionary 和 Set。在前面的示例中我们经常用到的 Numbers 和 String(它们的内容) 可以直接在 C++ 代码中使用,因为这两者也是 C++ 的数据类型(虽然实现上不同,但不妨碍两者通用)。但是其他类型的数据结构在 C++ 中并没有,那么当 Python 需要使用这些类型且与 C++ 代码有交互时,该如何处理呢?

在 Boost.Python 和 C++ 的观点中,这些 Pythonic 变量只是类对象的实例。Python 的每一个变量或方法都是一个 Python 对象,这些都具备各种属性,且 Python 赋予了这些对象这样或那样的内置方法,以使我们可以方便地使用它们。Boost.Python 库提供了一个名为 object 的类,它可以封装一个有效的 Python 对象并提供了一个与 Python 类似的接口。换言之,每一个 Python 对象可以作为 Boost.Python 的一个 object 类实例,同时 Boost.Python 提供了这个实例接近 Python 语法的操作的支持,这些支持主要以运算符重载的形式实现。比如:

Python C++ 说明
y = x.foo y = x.attr(“foo”); 获取 x 的属性值,通常是成员变量
x.foo = 1 x.attr(“foo”) = 1; 设置 x 的属性
y = x[z] y = x[z]; 列表/字典操作
x[z] = 1 x[z] = 1; 列表/字典操作
y = x[3:-1] y = x.slice(3,-1); 切片操作
y = x[3:] y = x.slice(3,_);
y = x[:-2] y = x.slice(_,-2);
z = x(1, y) z = x(1, y); 调用函数
z = x.f(1, y) z = x.attr(“f”)(1, y); 调用成员函数
not x !x 逻辑非
x and y x && y 逻辑与

Boost.Python 的目标之一是提供 C++ 和 Python 之间的双向映射,同时保持 Python 的感觉。 Boost.Python C++ 对象尽可能接近 Python。在上表的操作示例中,虽然不完全一致,但是 Boost.Python 尽量提供了符合 C++ 语法又与 Python 功能一致的功能。

上述 Python 数据类型,除 Set 外,Boost.Python 都将其视为一个类的实例。如何理解呢,比如一个 Python 变量 a = [1,2,3,4] 是一个 List 类型,那么在 Boost.Python 中 a 是一个 class list 实例。

Object 类是一个基类,提供了针对所有Python 对象的通用的操作方法,对于 Python 的常用的数据类型,Boost.Python 提供了基于 object 的与 Python 类型对应的派生类:

  • list
  • dict
  • tuple
  • str
  • long_
  • enum

注:目前不包含 Set 类型。

这些派生类与基类的关系如下图:
在这里插入图片描述
针对 Python 对象的封装,Boost.Python 提出了两个封装概念: ObjectWrapper 和 TypeWrapper,前者用于描述管理 Python 的对象,后者针对特定的 Python 对象进行优化和改进。

ObjectWrapper

ObjectWrapper 定义了两个概念,用于描述管理 Python 地向的类,并且旨在支持使用类似 Python 的语法。

ObjectWrapper 概念的模型将 object 作为公用基类并用于通过成员函数提供特殊的构造函数或其他功能。除非返回类型 R 本身是一个 TypeWrapper,否则它是形式的成员函数调用。
比如语句

x.some_function(a1, a2,...an)

等价于:

extract<R>(x.attr("some_function")(object(a1), object(a2),...object(an)))()

TypeWrapper

TypeWrapper 是对 ObjectWrapper 的改进,它与特定的 Python 类型 X 相关联。对于给定的 TypeWrapper T,有效的构造函数表达式为:

T(a1, a2,...an)

构建一个新的 T 对象,管理调用 X 的结果,参数对应于:

object(a1), object(a2),...object(an)

当用作封装 C++ 函数的参数或用作 extract<> 的模板参数时,只有关联 Python 类型的实例才会被视为匹配项。

警告

当返回类型为 TypeWrapper 时,特殊成员函数调用的结果是返回的对象可能与特定 Python 对象的类型不匹配,通常情况下这不是一个严重的问题;最坏的结果是在运行时检测到错误的时间会比其他情况稍晚一些。有关如何发生这种情况的示例,请注意 dict 成员函数 items 返回列表类型的对象。现在假设用户在 Python 中定义了这个 dict 子类:

>>> class mydict(dict):
...     def items(self):
...         return tuple(dict.items(self)) # return a tuple

由于 mydict 的实例是 dict 的实例,因此当用作包装 C++ 函数的参数时,boost::python::dict 可以接受 Python 类型 mydict 的对象。在这个对象上调用 items() 可以导致 boost::python::list 的实例,它实际上包含一个 Python 元组。随后尝试在此对象上使用列表方法(例如追加或任何其他变异操作)将引发与尝试从 Python 执行此操作时发生的相同异常。

Object 基类

Object 类是通用 Python 对象的封装类和其他 TypeWrapper 类的基类。object 有一个模板化的构造函数,可以使用与 call<> 的参数相同的底层机制将任何 C++ 对象转换为 Python。如果一个对象实例是在没有任何构造函数参数的情况下创建的,那么这个实例的值就是 None。

object 类封装 PyObject*。处理 PyObjects 的所有复杂性,例如管理引用计数,都由对象类处理。Boost.Python C++ 对象实际上可以从任何 C++ 对象显式构造。

先看一个简单的示例,这个 Python 代码片段:

def f(x, y):
     if (y == 'foo'):
         x[3:7] = 'bar'
     else:
         x.items += y(3, x)
     return x

def getfunc():
   return f;

可以通过以下方式使用 Boost.Python 工具在 C++ 中重写:

object f(object x, object y) {
     if (y == "foo")
         x.slice(3,7) = "bar";
     else
         x.attr("items") += y(3, x);
     return x;
}
object getfunc() {
    return object(f);
}

除了由于我们使用 C++ 编写代码而导致的外观差异之外,Python 编码人员应该立即明白外观和有熟悉的感觉。

Object 类提供了诸如切片,属性策略和操作符等功能的应用,详细内容参见:boost/python/object.hpp

Object 派生类

Object 派生类也就是前面提到的 TypeWrapper 的行为类似于真正的 Python 类型。例如:

str(1) ==> "1"

在适当的情况下,特定的派生对象具有相应的 Python 类型的方法。例如, dict 有一个 keys() 方法:

d.keys()

make_tuple 用于声明元组。例子:

make_tuple(123, 'D', "Hello, World", 0.0);

在 C++ 中,当 Boost.Python 对象用作函数的参数时,需要进行子类型匹配。例如,当如下声明的函数 f 被包装时,它将只接受 Python 的 str 类型和子类型的实例。

void f(str name)
{
    object n2 = name.attr("upper")();   // NAME = name.upper()
    str NAME = name.upper();            // better
    object msg = "%s is bigger than %s" % make_tuple(NAME,name);
}

更详细地说:

str NAME = name.upper();

说明我们提供 str 类型方法的版本作为 C++ 成员函数。

object msg = "%s is bigger than %s" % make_tuple(NAME,name);

说明我们可以在 Python 中编写与 “format” % x,y,z 等效的 C++,这很有用,因为在标准 C++ 中没有简单的方法可以做到这一点。

但是需要注意的是:大多数情况下,Python 和 C++ 之间的对象是拷贝过来的,如果修改某一个对象不会影响到另一个对象的值。

# Python:
>>> d = dict(x.__dict__)     # copies x.__dict__
>>> d['whatever'] = 3        # modifies the copy

// C++ 
dict d(x.attr("__dict__"));  // copies x.__dict__
d['whatever'] = 3;           // modifies the copy

class_ as objects

由于 Boost.Python 对象的动态特性,任何 class_ 也可能是这些类型之一!我们可以使用它来创建包装实例。例子:

object vec345 = (
    class_<Vec2>("Vec2", init<double, double>())
        .def_readonly("length", &Point::length)
        .def_readonly("angle", &Point::angle)
    )(3.0, 4.0);

assert(vec345.attr("length") == 5.0);

Creating boost::python::object from PyObject*

当我们希望 boost::python::object 管理指向 PyObject* pyobj 的指针时,可以:

boost::python::object o(boost::python::handle<>(pyobj));

在这种情况下,o 对象管理 pyobj,它不会增加构造时的引用计数。

否则,要参考使用 boost::python::borrowed

boost::python::object o(boost::python::handle<>(boost::python::borrowed(pyobj)));

在这种情况下,调用 Py_INCREF,因此当对象 o 超出范围时,pyobj 不会被破坏。

list class

暴露 Python 内置的列表类型的方法。下面定义的构造函数和成员函数的语义可以通过阅读 TypeWrapper 概念定义来完全理解。由于 list 是从 object 公开派生的,因此 public object 接口也适用于 list 实例。

该类定义在 <boost/python/list.hpp> 头文件中,部分代码 如下:

namespace boost { namespace python
{
  class list : public object
  {
   public:
      // 构造函数,生成一个 list 类实例
      list(); // new list
      template <class T>
      explicit list(T const& sequence);

      template <class T>
      void append(T const& x);    // 在列表末尾添加新的对象

      template <class T>
      long count(T const& value) const;   // 在列表末尾添加新的对象

      template <class T>
      void extend(T const& x);            // 在列表末尾一次性追加另一个序列中的多个值

      template <class T>
      long index(T const& x) const;     // 从列表中找出某个值第一个匹配项的索引位置

      template <class T>
      void insert(object const& index, T const& x); // insert object before index

      object pop(); // remove and return item at index (default last)
      object pop(long index);
      object pop(object const& index);

      template <class T>
      void remove(T const& value);       // 移除列表中 value 的第一个匹配项

      void reverse(); // reverse *IN PLACE*

      void sort(); //  sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1
      template <class T>
      void sort(T const& value);
  };
}}

Python list 支持的大部分方法在 Boost.Python list class 中都有实现,且为了尽量与 python 的习惯一致,函数调用方式和参数也都类似。

示例
下面以简单的 reverse() 函数为例,介绍 Boost.Python 的 list 类的使用
C++:

#include <iostream>

#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/return_by_value.hpp>
#include <boost/python/return_value_policy.hpp>

#include <boost/python/list.hpp>

namespace bp = boost::python;

bp::list list_reverse (bp::list &l1)
{
    std::cout << "Input list length: " << len(l1) << std::endl;

    l1.reverse();

    return l1;
}

BOOST_PYTHON_MODULE(objects)
{
    using namespace boost::python;

    def("list_reverse", list_reverse, args("l1"), return_value_policy<return_by_value>())
    ;
}

Python:

$ python
>>> import objects
>>> a = [1, 3, 6, 2, 9, 0]
>>> objects.list_reverse(a)
Input list length: 6
[0, 9, 2, 6, 3, 1]

好吧,这个示例很无聊,主要目的是说明当 python 接口传入一个 list 参数时,C++ 可以通过 Boost.Python 实现对该参数的处理。

dict class

dict 类是针对 python 字典数据类型封装的 object 衍生类,提供了 python dict 类似的方法和操作。定义在文件 <boost/python/dict.hpp> 中。

namespace boost { namespace python
{
   class dict : public object
   {
      dict();    // 构造函数
      template< class T >
      dict(T const & data);

      // modifiers
      void clear();     // 删除字典内所有元素
      dict copy();      // 返回一个字典的浅复制

      template <class T1, class T2>
      tuple popitem();  // 返回并删除字典中的最后一对键和值。

      // 和get()类似, 但如果键 k 不存在于字典中,将会添加键并将值设为 d 或者 default
      template <class T>
      object setdefault(T const &k);    
      template <class T1, class T2>
      object setdefault(T1 const & k, T2 const & d);

      // 把字典 E 的键/值对更新到dict里
      void update(object_cref E);
      template< class T >
      void update(T const & E);

      // 返回字典的所有值
      list values() const;

      // 返回指定键k的值,如果键不在字典中返回 d 或者默认值 NULL
      object get(object_cref k) const;
      template<class T>
      object get(T const & k) const;
      object get(object_cref k, object_cref d) const;
      object get(T1 const & k, T2 const & d) const;

      // 如果键在字典dict里返回true,否则返回false
      bool has_key(object_cref k) const;
      template< class T >
      bool has_key(T const & k) const;

      list items() const;         // 以列表返回字典的元组对象
      object iteritems() const;   // 以迭代器的形式返回字典元组
      object iterkeys() const;    // 以迭代器的形式返回字典关键字
      object itervalues() const;  // 以迭代器的形式返回字典值
      list keys() const;          // 返回字典的关键字列表
  };
}}

示例
C++:

bp::dict swap_object_dict(bp::object target, bp::dict d)
{
    bp::dict result = bp::extract<bp::dict>(target.attr("__dict__"));
    target.attr("__dict__") = d;
    
    return result;
}

这个函数会将输入参数中的字典 d 的内容和 target 的 __dict__ 属性互换,并将 target 的原有 __dict__ 属性作为新的字典输出。

Python:

import objects

class O1:
    ''' class O1 '''
    Name = 'Test'
    Age = 0

    def __init__(self):
        self.Name = 'Test'
        self.Age = 0

a = O1()
print(a.__dict__)

d1 = {"Name":"Xiangdi", "Age":10}
print(d1)

d2 = objects.swap_object_dict(a, d1)
print(a.__dict__)
print(d2)

运行结果:

$ python objects.py
{'Na': 'Test', 'Ag': 0}
{'Name': 'Xiangdi', 'Age': 10}
{'Name': 'Xiangdi', 'Age': 10}
{'Na': 'Test', 'Ag': 0}

tuple class

这个类封装了部分类似 Python tuple 数据类型的功能。但是相对于其他衍生类, class tuple 的函数相对较少,只有一个构造函数。特点在于提供了多种构造 tuple 的重载函数 make_tuple。其定义在 <boost/python/tuple.hpp> 中。

namespace boost { namespace python
{
   class tuple : public object
   {
      // tuple() -> an empty tuple
      tuple();

      // tuple(sequence) -> tuple initialized from sequence's items
      template <class T>
      explicit tuple(T const& sequence)
  };
}}

namespace boost { namespace python
{
  tuple make_tuple();

  template <class A0>
  tuple make_tuple(A0 const& a0);

  template <class A0, class A1>
  tuple make_tuple(A0 const& a0, A1 const& a1);
  ...
  template <class A0, class A1,...class An>
  tuple make_tuple(A0 const& a0, A1 const& a1,...An const& an);
}}

示例
C++:

bp::tuple head_and_tail_tuple (bp::object seq)
{
    return bp::make_tuple(seq[0],seq[-1]);
}

Python:

$ python
>>> import objects
>>> t1 = ("Ali", "Baidu", "Tencent", "Google")
>>> t2 = objects.head_and_tail_tuple(t1)
>>> print(t2)
('Ali', 'Google')

str class

str class 模拟了 Python 内置 str 类型的字符串方法。定义在 <boost/python/str.hpp> 中,其提供的函数如下:

namespace boost { namespace python
{
  class str : public object
  {
   public:
      // 构造函数
      str(); // new str
      str(char const* s); // new str
      str(char const* start, char const* finish); // new str
      str(char const* start, std::size_t length); // new str
      template <class T>
      explicit str(T const& other);

      // 将字符串的第一个字符转换为大写
      str capitalize() const;

      // 返回一个指定的宽度 width 居中的字符串
      template <class T>
      str center(T const& width) const;

      // 返回 sub 出现的次数,start 和 end表示指定的范围
      template<class T>
      long count(T const& sub) const;
      template<class T1, class T2>
      long count(T1 const& sub,T2 const& start) const;
      template<class T1, class T2, class T3>
      long count(T1 const& sub,T2 const& start, T3 const& end) const;

      // 对 str 进行解码
      object decode() const;
      template<class T>
      object decode(T const& encoding) const;
      template<class T1, class T2>
      object decode(T1 const& encoding, T2 const& errors) const;

      // 对 str 进行编码
      object encode() const;
      template <class T>
      object encode(T const& encoding) const;
      template <class T1, class T2>
      object encode(T1 const& encoding, T2 const& errors) const;

      // 检查字符串是否以 suffix 结束,其中 start 和 end 表示字符串范围
      template <class T>
      bool endswith(T const& suffix) const;
      template <class T1, class T2>
      bool endswith(T1 const& suffix, T2 const& start) const;
      template <class T1, class T2, class T3>
      bool endswith(T1 const& suffix, T2 const& start, T3 const& end) const;

      // 把字符串中的 tab 符号转为空格,tabsize 表示空格数,默认为8
      str expandtabs() const;
      template <class T>
      str expandtabs(T const& tabsize) const;

      // 从字符串中查找 sub 子串,其中 start 和 end 表示字符串范围
      template <class T>
      long find(T const& sub) const;
      template <class T1, class T2>
      long find(T1 const& sub, T2 const& start) const;
      template <class T1, class T2, class T3>
      long find(T1 const& sub, T2 const& start, T3 const& end) const;

      // 跟find()方法一样,只不过如果 sub 不在字符串中会报一个异常。
      template <class T>
      long index(T const& sub) const;
      template <class T1, class T2>
      long index(T1 const& sub, T2 const& start) const;
      template <class T1, class T2, class T3>
      long index(T1 const& sub, T2 const& start, T3 const& end) const;

      // 如果字符串 [...] 则返回 True, 否则返回 False
      bool isalnum() const;    // [至少有一个字符且所有字符都是字母或者数字]
      bool isalpha() const;    // [至少有一个字符并且所有字符都是字母或者汉字]
      bool isdigit() const;    // [只包含数字]
      bool islower() const;    // [包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写]
      bool isspace() const;    // [只包含空白]
      bool istitle() const;    // [是标题化的]
      bool isupper() const;    // [包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写]

      // 以指定字符串作为分隔符,将 sequence 中所有的元素(的字符串表示)合并为一个新的字符串
      template <class T>
      str join(T const& sequence) const;

      // 返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串。
      template <class T>
      str ljust(T const& width) const;
      
      // 转换字符串中所有大写字符为小写.
      str lower() const;
      
      // 截掉字符串左边的空格或指定字符。
      str lstrip() const;

      // 将字符串中的 old 替换成 new_,如果 maxsplit 指定,则替换不超过 maxsplit 次。
      template <class T1, class T2>
      str replace(T1 const& old, T2 const& new_) const;
      template <class T1, class T2, class T3>
      str replace(T1 const& old, T2 const& new_, T3 const& maxsplit) const;

      // 类似于 find(),不过是从右边开始.
      template <class T>
      long rfind(T const& sub) const;
      template <class T1, class T2>
      long rfind(T1 const& sub, T2 const& start) const;
      template <class T1, class T2, class T3>
      long rfind(T1 const& sub, T2 const& start, T3 const& end) const;

      // 类似于 index(),不过是从右边开始.
      template <class T>
      long rindex(T const& sub) const;
      template <class T1, class T2>
      long rindex(T1 const& sub, T2 const& start) const;
      template <class T1, class T2, class T3>
      long rindex(T1 const& sub, T2 const& start, T3 const& end) const;

      // 返回一个原字符串右对齐,并使用默认空格填充至长度 width 的新字符串
      template <class T>
      str rjust(T const& width) const;
    
      // 删除字符串末尾的空格或指定字符。
      str rstrip() const;

      // 以 sep 或者空格为分隔符截取字符串,如果 maxsplit 有指定值,则仅截取 maxsplit+1 个子字符串
      list split() const;
      template <class T>
      list split(T const& sep) const;
      template <class T1, class T2>
      list split(T1 const& sep, T2 const& maxsplit) const;

      // 按照行('\r', '\r\n', \n')分隔,返回一个包含各行作为元素的列表,如果参数 keepends 为 False,不包含换行符,如果为 True,则保留换行符。
      list splitlines() const;
      template <class T>
      list splitlines(T const& keepends) const;

      // 检查字符串是否是以指定子字符串 prefix 开头,是则返回 True,否则返回 False。如果start 和 end 指定值,则在指定范围内检查。
      template <class T>
      bool startswith(T const& prefix) const;
      template <class T1, class T2>
      bool startswidth(T1 const& prefix, T2 const& start) const;
      template <class T1, class T2, class T3>
      bool startswidth(T1 const& prefix, T2 const& start, T3 const& end) const;

      str strip() const;      // 在字符串上执行 lstrip()和 rstrip()
      str swapcase() const;   // 将字符串中大写转换为小写,小写转换为大写
      str title() const;      // 返回"标题化"的字符串,就是说所有单词都是以大写开始,其余字母均为小写

      // 根据 table 给出的表(包含 256 个字符)转换 string 的字符, 要过滤掉的字符放到 deletechars 参数中
      template <class T>
      str translate(T const& table) const;
      template <class T1, class T2>
      str translate(T1 const& table, T2 const& deletechars) const;

      str upper() const;  // 转换字符串中的小写字母为大写
  };
}}

从上面的定义中可以看出,Boost.Python str 类给出的函数与 Python 的原生方法是一一对应的,但是也要注意仍然没有 Python 原生方法灵活,比如有些函数缺乏部分参数,而且 zfill() isdecimal() 等方法并没有包含进来。

示例
C++:

bp::str set_str_as_title (bp::str x)
{
    return x.title();
}

Python:

$ python
>>> import objects
>>> s = "boost wrap str class"
>>> objects.set_str_as_title(s)
'Boost Wrap Str Class'

long class

对于数字,C++ 和 Python 基本上是可以通用的,而且 Python 的这一数据类型也没有什么额外的方法,从 long 的定义也没有看到有什么成员函数。<boost/python/long.hpp>

namespace boost { namespace python
{
  class long_ : public object
  {
   public:
      long_(); // new long_

      template <class T>
      explicit long_(T const& rhs);

      template <class T, class U>
      long_(T const& rhs, U const& base);
  };
}}

示例
对于 long class 类型和直接使用 int 在功能上有什么区别,到目前为止我还不是很了解。
看下面的示例:

namespace bp = boost::python;
int multile_int (int x)
{
    return x * 2;
}

bp::long_ multile_long (bp::long_ x)
{
    int y = bp::extract<int>(x);
    bp::long_ z(y*2);
    
    return z;
}

def("multile_int", multile_int, args("x"), return_value_policy<return_by_value>());
def("multile_long", multile_long, args("x"), return_value_policy<return_by_value>());

对于同样的 Python 参数,其返回值是一样的:

>>> import objects
>>> objects.multile_int(2)
4
>>> objects.multile_long(2)
4

但是需要注意的是在上例中 multile_int() 里的 x 以及返回值都是一个数值,在 C++ 程序里可以直接打印出来。而在 multile_long() 里的 z 是一个类的实例。虽然最后传递到 python 里会拷贝数值,但在 C++ 程序里是不能直接作为数值操作的。

enum class

虽然 Python 没有枚举类型,但是作为 C++ 的基本数据类型,在软件开发的过程中非常常见。在碰到这一类型时,我们经常希望将它作为 int 传递给 Python。 Boost.Python 同样也提供了枚举工具,同时负责从 Python 的动态类型到 C++ 的强静态类型的正确转换(在 C++ 中,int 不能隐式转换为枚举)。

这个类相关定义在 <boost/python/enum.hpp> 中。该头文件定义了用户向 Python 公开其 C++ 枚举类型的接口。其类定义如下:

namespace boost { namespace python
{
  template <class T>
  class enum_ : public object
  {
    enum_(char const* name, char const* doc = 0);    // 构造函数
    enum_<T>& value(char const* name, T x); // 将 x 值与 name 绑定,即设置 name 值为 x。返回类实例本身,即 *this
    enum_<T>& export_values();  // 将当前已定义的枚举值和名称公开,之后属性名称便可在 Python 中直接使用。
  };
}}

创建一个派生自 Python 的 int 类型的 Python 类,该类型与作为其第一个参数传递的 C++ 类型相关联。

enum 的使用与其他派生类不同,为了说明,给定一个 C++ 枚举:

enum choice { red, blue };

需要指定构造函数并将名称和值绑定:

enum_<choice>("choice")
    .value("red", red)
    .value("blue", blue)
    ;

之后 choice 及其元素便可以暴露给 Python。我们可以在 Python 中访问这些值:

>>> my_module.choice.red
my_module.choice.red

其中 my_module 是声明枚举的模块。您还可以围绕一个类创建一个新范围:

scope in_X = class_<X>("X")
                .def( ... )
                .def( ... )
            ;

// Expose X::nested as X.nested
enum_<X::nested>("nested")
    .value("red", red)
    .value("blue", blue)
    ;

新的枚举类型是在当前的 scope() 中创建的,它通常是当前的模块。上面的代码片段创建了一个派生自 Python 的 int 类型的 Python 类,该类型与作为其第一个参数传递的 C++ 类型相关联。

注:scope是一个具有关联的全局 Python 对象的类,该对象控制 Python 命名空间,其中新的扩展类和包装的函数将被定义为属性。其具体说明后面更。

示例
C++:

#include <boost/python/enum.hpp>
#include <boost/python/def.hpp>
#include <boost/python/module.hpp>

using namespace boost::python;

enum color { red = 1, green = 2, blue = 4 };

color identity_(color x) { return x; }

BOOST_PYTHON_MODULE(enums)
{
  enum_<color>("color")
    .value("red", red)
    .value("green", green)
    .export_values()
    .value("blue", blue)
    ;

  def("identity", identity_);
}

Python:

>>> from enums import *

>>> identity(red)
enums.color.red

>>> identity(color.red)
enums.color.red

>>> identity(green)
enums.color.green

>>> identity(color.green)
enums.color.green

>>> identity(blue)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'blue' is not defined

>>> identity(color.blue)
enums.color.blue

>>> identity(color(1))
enums.color.red

>>> identity(color(2))
enums.color.green

>>> identity(color(3))
enums.color(3)

>>> identity(color(4))
enums.color.blue

>>> identity(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: bad argument type for built-in operation

注意上面 Line15 和 Line3/9 的区别,在 Python 程序中可以直接使用 “red”, “green” 作为参数,而 “blue” 不行。这是因为在前面的构造函数中, export_values() 前面的 scope 只包含 “red” “green” 两个值。

参考资料

ObjectWrapper
Object Interface
Chapter 3. Object Wrappers
boost.python object
boost.python list


版权声明:本文为rangfei原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。