我有类似以下的类:
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做结合。
谢谢。
这是可能的__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
。 这不是最好的接口,该绑定被设计成提供所需的功能 ,而不是期望的接口 。
现在,让我们写一些简单的绑定,将创建类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
};
或者,如果你喜欢的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