Boost.Python教程:对象接口

Python是动态类型的,与静态类型的C ++不同。 Python变量可以包含整数,浮点数,列表,字典,元组,str,long等等。 从Boost.Python和C ++的角度来看,这些Pythonic变量只是类object实例。 我们将在本章中看到如何处理Python对象。

如前所述,Boost.Python的目标之一是在保持Python感觉的同时提供C ++和Python之间的双向映射。 Boost.Python C ++ object尽可能接近Python。 这应该显着减少学习曲线。

Boost.Python教程:对象接口_第1张图片

基本接口

object包装PyObject* 。 处理PyObject的所有复杂性如管理引用计数都由object类处理。 C ++对象的互操作性是无缝的。 事实上,Boost.Python C ++ object可以从任何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编码器来说很明显。

派生对象类型

Boost.Python附带了一组与Python相对应的派生object类型:

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

这些派生object类型就像真正的Python类型一样。 例如:

str(1) ==> "1"

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

d.keys()

make_tuple用于声明元组文字 。 例:

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

在C ++中,当Boost.Python object用作函数的参数时,需要子类型匹配。 例如,当包含一个函数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中编写C ++等效的"format" % x,y,z ,这很有用,因为在std C ++中没有简单的方法可以做到这一点。

 请注意忘记Python的大多数可变类型的构造函数都在复制中的常见缺陷,就像在Python中一样。

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_ 作为对象

由于Boost.Python对象的动态特性,任何class_也可能是这些类型中的一种! 以下代码片段包装了类(类型)对象。

我们可以使用它来创建包装实例。 例:

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

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

提取C ++对象

在某些时候,我们需要从对象实例中获取C ++值。 这可以通过extract功能来实现。 考虑以下:

double x = o.attr("length");   //编译错误

在上面的代码中,我们得到了编译器错误,因为Boost.Python object不能隐式转换为double s。 相反,我们想要做的就是通过写:

double l = extract(o.attr("length"));
Vec2& v = extract(o);
assert(l == v.length());

第一行尝试提取Boost.Python object的“length”属性。 第二行尝试从Boost.Python object 提取 Vec2 object 。

请注意我们上面说“尝试”。 如果Boost.Python object实际上不包含Vec2类型怎么办? 考虑到Python object的动态特性,这当然是一种可能性。 为安全起见,如果无法提取C ++类型,则会引发相应的异常。 为避免异常,我们需要测试可提取性:

extract x(o);
if (x.check()) {
    Vec2& v = x(); ...

 精明的读者可能已经注意到, extract工具实际上解决了可变复制问题:

dict d = extract(x.attr("__dict__"));
d["whatever"] = 3;          // modifies x.__dict__ !

枚举

Boost.Python有一个很好的工具来捕获和包装C ++枚举。 虽然Python没有enum类型,但我们经常希望将我们的C ++枚举作为int公开给Python。 Boost.Python的枚举工具使得这很简单,同时处理从Python的动态类型到C ++的强静态类型的正确转换(在C ++中,整数不能隐式转换为枚举)。 为了说明,给定一个C ++枚举:

enum choice { red, blue };

构造:

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

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

[注意] 注意

什么是范围?

范围是一个具有关联的全局Python对象的类,该对象控制Python命名空间,其中新的扩展类和包装函数将被定义为属性。 细节可以在这里找到。

您可以在Python中访问这些值

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

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

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

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

从PyObject *创建boost :: python :: object

当你想要一个boost :: python :: object来管理一个指向PyObject * pyobj的指针时,你会这样做:

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

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

否则,使用借来的参考:

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

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

你可能感兴趣的:(Boost,#,Python)