C++/C# Python Hook
我在网上和Stack Overflow上找了很久,但还是没找到我想要的答案。有没有办法在C++或C#中调用Python函数?我想捕捉到这个函数的调用和它的参数。
下面是一个例子:
def a(pOne):
//做一些事情
def b():
a("a")
在调用'a'的时候,我想让C++能够拦截这个函数(假设我知道函数的名字),然后在调用'a'的时候执行一个C++的函数或事件,并把传给C++函数或事件的参数设置为传给'pOne'的值。
谢谢!
3 个回答
你有没有什么理由不想修改Python代码?如果没有,那为什么不写一个扩展模块或者用Python的ctypes来调用你的dll呢?然后用一个装饰器把a
包裹起来,这样就可以调用你的钩子了?
import my_cpp_module
def hook_a_to_call_my_cpp_module(orig_a):
def replacement_a(pOne):
try:
my_cpp_module.my_cpp_function(pOne)
finally:
return orig_a(pOne)
#
return replacement_a
@hook_a_to_call_my_cpp_module
def a(pOne):
pass
你甚至可以在不修改包含a
的源文件的情况下做到这一点,像这样:
# in this other file you import the module that contains function a
import the_py_that_has_a
# next line just to avoid copy-pasting the above code from my answer here again
from the_decorator_from_the_previous_example import hook_a_to_call_my_cpp_module
the_py_that_has_a.a = hook_a_to_call_my_cpp_module(the_py_that_has_a.a)
如果你有一些理由完全不想修改你的Python代码,能不能在评论里说一下?
如果你只是想写一些C++代码,然后让Python能调用这些代码,你可以通过扩展和嵌入Python解释器来实现。(实际上,只需要扩展那部分,另一部分可以忽略。)
简单来说,如果你写了一个新的模块叫foo
,那么任何人都可以通过import foo
来使用它,并调用foo.a("a")
。无论这个模块是用Python文件foo.py
实现的,还是从foo.cpp
编译成动态库foo.so
,都没关系。
为了实现这一点,正如Kyle C所建议的,有很多更高级的方法可以做到这一点,这样你就不需要直接处理C的API了。(除了他提到的Boost.Python,我还建议你看看Cython
,它允许你用一种几乎是Python的语言来编写代码,并且可以直接与C++交互,同时也能直接暴露给Python使用。)
不过,这并不是你想要的。你想做的是把某个在Python代码中定义的函数,连接到一个不同的功能上。为此,你确实需要阅读上面提到的扩展和嵌入文档。你需要写一个嵌入式解释器,重现一些行为(具体需要重现多少取决于你想要连接的地方),并确保在调用PyObject_Call
或类似函数之前,先检查这个对象是否是a
函数,如果是,就调用你的连接代码。
这会相当困难。如果你还没有写过嵌入式Python解释器,建议你先去做这个,再考虑如何连接。
值得注意的是,从Python内部进行连接可能要简单得多,而不是从解释器外部进行。(如果你从来没有听说过“猴子补丁”,可以去搜索一下。)你总是可以让你的Python连接代码调用一个你用C++构建的模块,或者直接通过ctypes
调用一个编译好的.so文件。
最后,如果你想在运行时连接某个正在运行的解释器实例,那就更难了。你显然需要能够进行某种调试/跟踪等操作,并插入代码,这些细节完全取决于你的平台。然后,你需要做的事情和之前的困难版本类似,只不过你需要先拦截对PyObject_Call
的调用(来自libpython.so
/Python.dll
/Python.framework
等),让它先通过你的连接代码。如果你还不知道如何在你的平台上拦截SO调用,你需要先学习这个,然后才能考虑如何从外部连接Python代码。
根据你想做的事情,C++有几种不同的选择。
实现你想要的功能最好的方法是“扩展”Python。
我最喜欢的方式是使用Boost.Python。
你也可以使用原生的C-API,不过我不太推荐这样做。
如果你想看看使用Boost.Python进行嵌入和扩展的例子,可以查看我正在做的这个项目。