For*_*ang 6 c++ python reference python-sip
我有一个类似于以下的类:
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]; }
}
Run Code Online (Sandbox Code Playgroud)
我想让以下Python代码工作:
a = A()
a.x[0] = 4
print a.x[0]
Run Code Online (Sandbox Code Playgroud)
我在想的__setattr__和__getattr__,但如果它的工作原理不清楚.另一种方法是实现以下Python:
a = A()
a['x', 0] = 4
print a['x', 0]
Run Code Online (Sandbox Code Playgroud)
不如前一个好,但可能更容易实现(用__slice__?).
PS.我正在使用sip进行绑定.
谢谢.
这是可能的__getattr__和定制%MethodCode; 但是,有几点需要考虑:
a.x它将返回一个提供__getitem__和的对象__setitem__.这两种方法都应该在IndexError出现超出范围时引发,因为这是用于迭代通过的旧协议的一部分__getitem__; 没有它,迭代时会发生崩溃a.x.为了保证向量的生命周期,a.x对象需要维护对拥有vector(a)的对象的引用.请考虑以下代码:
a = A()
x = a.x
a = None # If 'x' has a reference to 'a.v' and not 'a', then it may have a
# dangling reference, as 'a' is refcounted by python, and 'a.v' is
# not refcounted.
Run Code Online (Sandbox Code Playgroud)写入%MethodCode可能很困难,尤其是在错误情况下必须管理引用计数时.它需要了解python C API和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
Run Code Online (Sandbox Code Playgroud)
在进行绑定之前,让我们检查一下A界面.虽然它是一个在C++中使用的简单界面,但它在python中有一些困难:
n = a.x[0]绑定n引用float从中返回的 对象a.x[0].赋值n = 4重新n引用以引用int(4)对象; 它不设置a.x[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
Run Code Online (Sandbox Code Playgroud)
大! PyA现在提供不返回引用的成员函数,并返回length作为int.它不是最好的接口,绑定被设计为提供所需的功能,而不是所需的接口.
现在,让我们编写一些将A在cexample模块中创建类的简单绑定.
这是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
};
Run Code Online (Sandbox Code Playgroud)
或者如果您更喜欢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 )
;
}
Run Code Online (Sandbox Code Playgroud)
由于PyA中间类,两个绑定都相当简单.此外,这种方法需要较少的SIP和Python C API知识,因为它需要较少的%MethodCode块内代码.
最后,创建example.py将提供所需的pythonic接口:
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 )
Run Code Online (Sandbox Code Playgroud)
最后,绑定提供了我们需要的功能,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