蟒对C ++结合运算符重载(Python binding for C++ operator over

2019-09-19 10:00发布

我有类似以下的类:

class A {
    vector<double> v;
    double& x(int i) { return v[2*i]; }
    double& y(int i) { return v[2*i+1]; }
    double x(int i) const { return v[2*i]; }
    double y(int i) const { return v[2*i+1]; }
}

我想有以下Python代码的工作:

a = A()
a.x[0] = 4
print a.x[0]

我在想__setattr____getattr__ ,但如果它的工作原理不清楚。 另一种方法是执行下面的Python:

a = A()
a['x', 0] = 4
print a['x', 0]

还不如前一个,但可能会更容易实现(与__slice__ ?)。

PS。 我使用SIP做结合。

谢谢。

Answer 1:

这是可能的__getattr__和定制%MethodCode ; 但是,也有考虑到几点:

  • 中间型/对象需要被创建,如ax将返回一个对象,它提供__getitem____setitem__ 。 这两种方法都应该提高的IndexError时出界发生,因为这是用于通过迭代旧协议的一部分__getitem__ ; 没有它,遍历时会发生崩溃ax
  • 为了保证载体的寿命,所述ax对象需要保持到拥有向量(的对象的引用a )。 考虑下面的代码:

     a = A() x = ax a = None # If 'x' has a reference to 'av' and not 'a', then it may have a # dangling reference, as 'a' is refcounted by python, and 'av' is # not refcounted. 
  • %MethodCode期间具有错误的情况来管理引用计数尤其是很困难的。 它需要蟒蛇C API和SIP的理解。

对于一个替代解决方案,可以考虑:

  • 设计Python绑定来提供功能。
  • 在python设计类(ES)提供使用绑定的Python的接口。

虽然这种方法有一些缺点,如代码分为可能需要与库分布多个文件,但它确实提供了一些重要的好处:

  • 这是很容易实现在Python Python的接口比C或互操作性库的接口。
  • 对切​​片,迭代器等支持可在python被更自然地实现,而不必通过C API来管理它。
  • 可以利用Python的垃圾收集器来管理底层存储器的寿命。
  • 的Python的接口是从任何执行被用来提供Python和C之间的互操作性++解耦。 与较平坦的和更简单的结合界面,实现方式中,如Boost.Python的和SIP之间的转换,要容易得多。

下面是一个步行通过展示这种方法。 首先,我们先从基本的A类。 在这个例子中,我提供了一个构造函数,将设置一些初始数据。

a.hpp

#ifndef A_HPP
#define A_HPP

#include <vector>

class A
{
  std::vector< double > v;
public:
  A() { for ( int i = 0; i < 6; ++i ) v.push_back( i ); }
  double& x( int i )         { return v[2*i];       }
  double  x( int i ) const   { return v[2*i];       }
  double& y( int i )         { return v[2*i+1];     }
  double  y( int i ) const   { return v[2*i+1];     }
  std::size_t size() const   { return v.size() / 2; }
};

#endif  // A_HPP

做绑定之前,让我们看的A接口。 虽然这是一个简单的界面在C ++中使用,它在蟒蛇一些困难:

  • Python不支持重载方法,成语支持重载时的参数类型/数是相同的将失败。
  • 为双(浮在Python)的参考的概念是在两种语言之间不同。 在Python,浮子是一个不可变型,因此它的值不能被改变。 例如,在Python语句n = ax[0]结合n引用float对象从返回ax[0] 赋值n = 4重新绑定n引用int(4)对象; 它不设置ax[0]4
  • __len__预计int ,不是std::size_t

让我们创建一个基本的中级班,这将有助于简化绑定。

pya.hpp

#ifndef PYA_HPP
#define PYA_HPP

#include "a.hpp"

struct PyA: A
{
  double get_x( int i )           { return x( i ); }
  void   set_x( int i, double v ) { x( i ) = v;    }
  double get_y( int i )           { return y( i ); }
  void   set_y( int i, double v ) { y( i ) = v;    }
  int    length()                 { return size(); }
};

#endif // PYA_HPP

大! PyA现在提供成员函数不返回引用和长度会返回一个int 。 这不是最好的接口,该绑定被设计成提供所需的功能 ,而不是期望的接口

现在,让我们写一些简单的绑定,将创建类Acexample模块。

这里是SIP的绑定:

%Module cexample

class PyA /PyName=A/
{
%TypeHeaderCode
#include "pya.hpp"
%End
public:
  double get_x( int );
  void set_x( int, double );
  double get_y( int );
  void set_y( int, double );
  int __len__();
  %MethodCode
    sipRes = sipCpp->length();
  %End
};

或者,如果你喜欢的Boost.Python:

#include "pya.hpp"
#include <boost/python.hpp>

BOOST_PYTHON_MODULE(cexample)
{
  using namespace boost::python;
  class_< PyA >( "A" )
    .def( "get_x",   &PyA::get_x  )
    .def( "set_x",   &PyA::set_x  )
    .def( "get_y",   &PyA::get_y  )
    .def( "set_y",   &PyA::set_y  )
    .def( "__len__", &PyA::length )
    ;
}

由于PyA的中间阶层,无论绑定的是相当简单的。 此外,该方法需要较少的SIP和Python C API的知识,因为它需要较少的内代码%MethodCode块。

最后,创建example.py将提供所需的Python的界面:

class A:
    class __Helper:
        def __init__( self, data, getter, setter ):
            self.__data   = data
            self.__getter = getter
            self.__setter = setter

        def __getitem__( self, index ):
            if len( self ) <= index:
                raise IndexError( "index out of range" )
            return self.__getter( index )

        def __setitem__( self, index, value ):
            if len( self ) <= index:
                raise IndexError( "index out of range" )
            self.__setter( index, value )

        def __len__( self ):
            return len( self.__data )

    def __init__( self ):
        import cexample
        a = cexample.A()
        self.x = A.__Helper( a, a.get_x, a.set_x )
        self.y = A.__Helper( a, a.get_y, a.set_y )

最后,绑定提供我们所需要的功能和Python创造我们想要的界面 。 这是可能有绑定提供的接口; 然而,这可能需要两种语言和绑定实现之间的区别了丰富的认识。

>>> from example import A
>>> a = A()
>>> for x in a.x:
...   print x
... 
0.0
2.0
4.0
>>> a.x[0] = 4
>>> for x in a.x:
...   print x
... 
4.0
2.0
4.0
>>> x = a.x
>>> a = None
>>> print x[0]
4.0


文章来源: Python binding for C++ operator overloading