Python方法提高函数性能

5 投票
1 回答
3476 浏览
提问于 2025-04-15 18:44

我有一个方法是通过boost python导出到Python的,它接受一个boost::function作为参数。

根据我了解到的,boost::python应该可以很顺利地支持boost::function,但当我尝试用Python的方法调用这个函数时,却出现了这个错误

Boost.Python.ArgumentError: Python argument types in
    Class.createTimer(Class, int, method, bool)
did not match C++ signature:
    createTimer(class Class {lvalue}, unsigned long interval, 
    class boost::function<bool _cdecl(void)> function, bool recurring=False)

我在Python中用这段代码调用它

self.__class.createTimer( 3, test.timerFunc, False )

而在C++中,它是这样定义的

boost::int32_t createTimer( boost::uint32_t interval, boost::function< bool() > function, bool recurring = false );

这里的目标是创建一个计时器类,我希望能做到类似这样的事情

class->createTimer( 3, boost::bind( &funcWithArgs, arg1, arg2 ) )

来创建一个计时器,执行funcWithArgs。多亏了boost bind,这个方法几乎可以与任何函数或方法一起使用。

那么,我需要使用什么语法才能让boost::python接受我的Python函数作为boost::function呢?

1 个回答

12

我在Python的邮件列表上得到了一个答案,经过一些修改和更多的研究,我终于得到了我想要的结果 :)

我之前看到过mithrandi的帖子,但我不太喜欢那种声明函数的方式。通过一些巧妙的包装和一点Python的魔法,这样做既能实现功能,又能看起来不错!

首先,用这样的代码来包装你的Python对象:

struct timer_func_wrapper_t
{
    timer_func_wrapper_t( bp::object callable ) : _callable( callable ) {}

    bool operator()()
    {
        // These GIL calls make it thread safe, may or may not be needed depending on your use case
        PyGILState_STATE gstate = PyGILState_Ensure();
        bool ret = _callable();
        PyGILState_Release( gstate );
        return ret;
    }

    bp::object _callable;
};

boost::int32_t createTimerWrapper( Class* class, boost::uint64_t interval, bp::object function, bool recurring = false )
{
    return class->createTimer( interval, boost::function<bool ()>( timer_func_wrapper_t( function ) ), recurring );
}

在你的类里面,像这样定义方法:

.def( "createTimer", &createTimerWrapper, ( bp::arg( "interval" ), bp::arg( "function" ), bp::arg( "recurring" ) = false ) )

有了这点小包装,你就可以像这样施展魔法:

import MyLib
import time

def callMePls():
    print( "Hello world" )
    return True

class = MyLib.Class()

class.createTimer( 3, callMePls )

time.sleep( 1 )

为了完全模拟C++,我们还需要一个boost::bind的实现,可以在这里找到: http://code.activestate.com/recipes/440557/

这样,我们现在可以做类似这样的事情:

import MyLib
import time

def callMePls( str ):
    print( "Hello", str )
    return True

class = MyLib.Class()

class.createTimer( 3, bind( callMePls, "world" ) )

time.sleep( 1 )

编辑:

我喜欢在能的时候跟进我的问题。我之前成功使用了这段代码一段时间,但我发现当你想在对象构造函数中使用boost::function时,这种方法就不太管用了。虽然有办法让它类似于这样工作,但你构造的新对象最终会有不同的签名,无法与其他类似的对象一起使用。

这让我感到困扰,于是我决定解决这个问题。现在我对boost::python了解得更多了,我想出了一个相当不错的“通用”解决方案,使用了转换器。这段代码可以将一个Python可调用对象转换为boost::python< bool() >对象,并且可以很容易地修改为转换为其他boost函数。

// Wrapper for timer function parameter
struct timer_func_wrapper_t
{
    timer_func_wrapper_t( bp::object callable ) : _callable(callable) {}

    bool operator()()
    {
        return _callable();
    }

    bp::object _callable;
};

struct BoostFunc_from_Python_Callable
{
    BoostFunc_from_Python_Callable()
    {
        bp::converter::registry::push_back( &convertible, &construct, bp::type_id< boost::function< bool() > >() );
    }

    static void* convertible( PyObject* obj_ptr )
    {
        if( !PyCallable_Check( obj_ptr ) ) return 0;
        return obj_ptr;
    }

    static void construct( PyObject* obj_ptr, bp::converter::rvalue_from_python_stage1_data* data )
    {
        bp::object callable( bp::handle<>( bp::borrowed( obj_ptr ) ) );
        void* storage = ( ( bp::converter::rvalue_from_python_storage< boost::function< bool() > >* ) data )->storage.bytes;
        new (storage)boost::function< bool() >( timer_func_wrapper_t( callable ) );
        data->convertible = storage;
    }
};

然后在你的初始化代码中,也就是BOOST_PYTHON_MODULE(),只需通过创建结构来注册这个类型:

BOOST_PYTHON_MODULE(Foo)
{
    // Register function converter
    BoostFunc_from_Python_Callable();

撰写回答