使用boost python调用具有可变数量输入参数的Python函数

时间:2012-08-10 14:42:17

标签: c++ python boost-python

是否可以从boost :: python中调用一个具有可变数量输入参数的python函数:

在python中我定义了一个函数

def py_fn1(a):
    return 2 * a

def py_fn2(a, b):
    return a+b

def call_fn_py(fn, *args):
    return fn(*args)

a = call_fn_py(py_fn1, 15)  # returns 30
b = call_fn_py(py_fn2, 10, 11)  # returns 21

根据评论给出结果。我想使用boost python在C ++中实现call_fn(fn,* args)。到目前为止,我有:

using namespace boost::python;
object call_fn(object fn, tuple arg_in)
{
return fn(arg_in);
}


BOOST_PYTHON_MODULE(python_addins)
{
    using namespace boost::python;
def("call_fn", &call_fn);
}

然后做类似的事情:

import python_addins

d = python_addins.call_fn(py_fn1, (5,))  # returns (5,5)

这不是我以后的事。如何编写call_fn以表现得像call_fn_py?

谢谢,Mark

1 个答案:

答案 0 :(得分:1)

可以使用可变数量的参数调用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中更容易进行元组打包和解包,因此可以在python中修补example.call_fn,以便函数参数使用辅助函数进行修饰,该函数在委托之前解包参数。

在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