可以使用可变数量的参数调用 python 函数。有两种方法:
- 通过创建一组
call_fn
函数来模拟可变数量的参数。
- 利用 python 将多个对象打包和解包到一个元组中的能力。
第一种方法对于通用 C++ 编程相当普遍。它需要创建一组根据参数数量变化的重载函数:call_fn( fn )
, call_fn( fn, A1 )
, call_fn( fn, A1, ...An)
. 使用 C++11 可变参数模板函数可以减少样板代码的数量。但是,在定义包装器时,必须指定模板函数的确切类型。因此,这种方法将根据包装的模板实例化限制参数的数量。
#include <boost/python.hpp>
using boost::python;
object call_fn1( object fn, object a1 )
{
return fn( a1 );
}
object call_fn2( object fn, object a1, object a2 )
{
return fn( a1, a2 );
}
object call_fn3( object fn, object a1, object a2, object a3 )
{
return fn( a1, a2, a3 );
}
BOOST_PYTHON_MODULE(example)
{
def( "call_fn", &call_fn1 );
def( "call_fn", &call_fn2 );
def( "call_fn", &call_fn3 );
}
这是一个演示:
>>> def py_fn1( a ): return 2 * a
...
>>> def py_fn2( a, b ): return a + b
...
>>> def call_fn_py( fn, *args ):
... from example import call_fn
... return call_fn( fn, *args )
...
>>> call_fn_py( py_fn1, 15 )
30
>>> call_fn_py( py_fn2, 10, 11 )
21
第二种方法利用元组。它要求调用者将参数打包成一个元组,并要求调用函数解包参数。但是,由于在 python 中进行元组打包和解包更容易,example.call_fn
可以在 python 中进行修补,以便函数参数用辅助函数装饰,该函数在委托之前解包参数。
在 C++ 中,创建一个call_fn
接受单个boost::python::tuple
参数的函数。
#include <boost/python.hpp>
using namespace boost::python;
object call_fn( object fn, tuple args )
{
return fn( args );
}
BOOST_PYTHON_MODULE(example)
{
def( "call_fn", &call_fn );
}
现在,创建example_ext.py
将补丁example.call_fn
:
import example
def patch_call_fn():
# Store handle to unpatched call_fn.
original = example.call_fn
# Helper function to create a closure that unpacks arguments
# before delegating to the user function.
def unpack_args( fn ):
def wrapper( args ):
return fn( *args )
return wrapper
# The patched function that will wrap the user function.
def call_fn( fn, *args ):
return original( unpack_args( fn ), args )
return call_fn
# Patch example.
example.call_fn = call_fn = patch_call_fn()
可以使用相同的演示,唯一需要的更改是example_ext
需要导入而不是example
.
>>> def py_fn1( a ): return 2 * a
...
>>> def py_fn2( a, b ): return a + b
...
>>> def call_fn_py( fn, *args ):
... from example_ext import call_fn
... return call_fn( fn, *args )
...
>>> call_fn_py( py_fn1, 15 )
30
>>> call_fn_py( py_fn2, 10, 11 )
21