『Python CoolBook』C扩展库_其六_从C语言中调用Python代码

一、C语言运行pyfun的PyObject对象

思路是在C语言中提供实参,传给python函数:

  • 获取py函数对象(PyObject),函数参数(C类型)
  • 获取GIL(PyGILState_Ensure)
  • 确保fun对象可调用
  • 参数转换为python对应类型(Py_BuildValue)
  • 调用python函数(PyObject_Call)
  • 确定调用无异常
  • 检查返回值
  • 释放GIL(PyGILState_Release)
  • 异常处理
#include "Python.h"

/* Execute func(x,y) in the Python interpreter.  The
   arguments and return result of the function must
   be Python floats */

double call_func(PyObject *func, double x, double y) {
  PyObject *args;
  PyObject *kwargs;
  PyObject *result = 0;
  double retval;

  /* Make sure we own the GIL */
  PyGILState_STATE state = PyGILState_Ensure();

  /* Verify that func is a proper callable */
  /* 你必须先有一个表示你将要调用的Python可调用对象。 这可以是一个函数、
     类、方法、内置方法或其他任意实现了 __call__() 操作的东西。 为了确
     保是可调用的,可以像下面的代码这样利用 PyCallable_Check() 做检查 */
  if (!PyCallable_Check(func)) {
    fprintf(stderr,"call_func: expected a callable\n");
    goto fail;
  }
  /* Build arguments */
  /* 使用 Py_BuildValue()构建参数元组或字典 */
  args = Py_BuildValue("(dd)", x, y);
  kwargs = NULL;

  /* Call the function */
  /* 使用 PyObject_Call(),传一个可调用对象给它、一个参数元组
     和一个可选的关键字字典。
     如果没有关键字参数,传递NULL */
  result = PyObject_Call(func, args, kwargs);
  /* 需要确保使用了 Py_DECREF() 或者 Py_XDECREF() 清理参数。
     第二个函数相对安全点,因为它允许传递NULL指针(直接忽略它),
     这也是为什么我们使用它来清理可选的关键字参数。 */
  Py_DECREF(args);
  Py_XDECREF(kwargs);

  /* Check for Python exceptions (if any) */
  /* 调用万Python函数之后,用PyErr_Occurred() 函数检查是否
     有异常发生 */
  if (PyErr_Occurred()) {
    PyErr_Print();
    goto fail;
  }

  /* Verify the result is a float object */
  if (!PyFloat_Check(result)) {
    fprintf(stderr,"call_func: callable didn‘t return a float\n");
    goto fail;
  }

  /* Create the return value */
  retval = PyFloat_AsDouble(result);
  Py_DECREF(result);

  /* Restore previous GIL state and return */
  PyGILState_Release(state);
  return retval;

fail:
  Py_XDECREF(result);
  PyGILState_Release(state);
  abort();   // Change to something more appropriate
}

要注意的是每一个 PyGILState_Ensure() 调用必须跟着一个匹配的 PyGILState_Release() 调用——即便有错误发生。 在这里,我们使用一个 goto 语句看上去是个可怕的设计, 但是实际上我们使用它来讲控制权转移给一个普通的exit块来执行相应的操作。 在 fail: 标签后面的代码和Python的 fianl: 块的用途是一样的。

二、使用模块名和方法名获取pyfun的PyObject对象

  • 获取模块名字符串,方法名字符串
  • 模块名转化为python的字符串类型(PyUnicode_FromString)
  • 模拟python的import行为(PyImport_Import),这是因为我们想经由python的逻辑获取函数
  • 由python的module获取方法(PyObject_GetAttrString),这个API获取方法使用的是C字符串
  • 返回方法,时python的对象类型
/* Load a symbol from a module */
PyObject *import_name(const char *modname, const char *symbol) {
  PyObject *u_name, *module;
  u_name = PyUnicode_FromString(modname);
  module = PyImport_Import(u_name);
  Py_DECREF(u_name);
  return PyObject_GetAttrString(module, symbol);
}

三、C模拟Python运行

  • 初始化python环境(Py_Initialize)
  • 导入模块获取方法(见本文第二部分)为PyObject
  • 调用方法PyObject(见本文第一部分)
  • 结束python环境(Py_Finalize)
/* Simple embedding example */
int main() {
  PyObject *pow_func;
  double x;

  Py_Initialize();
  /* Get a reference to the math.pow function */
  pow_func = import_name("math","pow");

  /* Call it using our call_func() code */
  for (x = 0.0; x < 10.0; x += 0.1) {
    printf("%0.2f %0.2f\n", x, call_func(pow_func,x,2.0));
  }
  /* Done */
  Py_DECREF(pow_func);
  Py_Finalize();
  return 0;
}

编译运行,

gcc -g embed.c -I/home/hellcat/anaconda3/include/python3.6m           -L/home/hellcat/anaconda3/lib/python3.6/config-3.6m-x86_64-linux-gnu -lpython3.6m

四、将可调用PyObject用C重新封装调用

这是个意义不大功能,只是展示C API中PyObject本质运行逻辑——PyObject可以代指任何Python中的对象,这里是它接收函数的例子:

/* Extension function for testing the C-Python callback */
static PyObject *py_call_func(PyObject *self, PyObject *args) {
  PyObject *func;

  double x, y, result;
  if (!PyArg_ParseTuple(args,"Odd", &func,&x,&y)) {
    return NULL;
  }
  result = call_func(func, x, y);
  return Py_BuildValue("d", result);
}

把它写到前一节中的pysample.c中,有如下效果

>>> import sample
>>> def add(x,y):
...     return x+y
...
>>> sample.call_func(add,3,4)
7.0
>>>

原文地址:https://www.cnblogs.com/hellcat/p/9093602.html

时间: 2024-10-07 19:51:45

『Python CoolBook』C扩展库_其六_从C语言中调用Python代码的相关文章

『Python CoolBook』C扩展库_其六_线程

GIL操作 想让C扩展代码和Python解释器中的其他进程一起正确的执行, 那么你就需要去释放并重新获取全局解释器锁(GIL). 在Python接口封装中去释放并重新获取全局解释器锁(GIL),此时本段程序失去GIL运行,其他线程可以无视本函数的运行而运行,直到Py_END_ALLOW_THREADS: #include "Python.h" ... PyObject *pyfunc(PyObject *self, PyObject *args) { ... Py_BEGIN_ALLO

在Julia语言中调用Python函数

在PyCall扩展包中,模仿Python的import语句,提供了一个可以导入Python模块的@pyimport宏.并且,为能在Julia中使用模块内的函数和常量做了封装,以及支持在Julia与Python间的自动类型转换. 同时,它还提供了对Python对象进行底层操作的设施.其中包括能与不透明的Python对象相对应的'PyObjec'类型,以及在Julia语言中对Python函数进行调用且做类型转换的pycall. 安装 在Julia中,只需要使用Pkg.add("PyCall"

c#中调用python

1. 安装IronPython,到http://ironpython.codeplex.com/下载安装包 2. 创建项目 创建一个C#的控制台应用程序. 添加引用: 浏览到IronPython的安装目录中,添加对IronPython.dll,Microsoft.Scripting.dll 两个dll的引用. 3. 添加Python文件到当前的项目中 创建一个文本文件命名为:hello.py, 编辑如下 def welcome(name):    return "hello" + na

Python学习笔记之六:在VS中调用Python

1,安装配置好Python本身的运行环境,以能在命令行下运行py脚本为准 2,将Python的根目录下的include文件夹,添加到VS的项目属性->配置属性->C/C++->"附加包含目录"中 3,将Python的根目录下的libs文件夹,添加到VS的项目属性->配置属性->链接器->"附加库目录"中 4,在C++项目中添加Python头文件: #include <python.h> 5,添加必要的Python初始化

Windows平台下如何在C#中调用Python

最近迷上了Python,发现它能够做很多C#无法完成的事情,比如,调用CMD或者在CMD中执行一个exe文件命令行并获得输出的结果.过程简单,处理起来也非常方便,但如果要用C#调用Python文件呢,没关系,你想到的肯定早就有也人想到过.网上Google一下,超级多.索性拿来实践吧. 首先要用到的就是这个软件:IronPython,官方下载地址:http://ironpython.codeplex.com 安装在Windows下之后去它的安装地址查找下面这两个文件: IronPython.dll

如何在Java中调用Python代码

有时候,我们会碰到这样的问题:与A同学合作写代码,A同学只会写Python,而不会Java, 而你只会写Java并不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方设法“调用对方的代码”. 下面我将举一些简单的小例子,借此说明:如何在Java中调用Python代码. 看懂这篇文章只需要具备: 熟悉Java的基本语法 懂一点点Python 主要内容如下: 什么是Jython? 一个HelloPython程序 在Jvm中执行Python脚本 仅在Java中调用Python

『Python CoolBook』使用ctypes访问C代码_下

这一次我们尝试一下略微复杂的c程序. 一.C程序 头文件: #ifndef __SAMPLE_H__ #define __SAMPLE_H__ #include <math.h> #ifdef __cplusplus extern "C" { #endif int gcd(int x, int y); int in_mandel(double x0, double y0, int n); int divide(int a, int b, int *remainder); d

Python之安装第三方扩展库

PyPI 地址:https://pypi.python.org/pypi 如果你知道你要找的库的名字,那么只需要在右上角搜索栏查找即可. 1.pip安装扩展库 (1)安装最新版本的扩展库: cmd> pip install django (2)安装指定版本的扩展库: cmd> pip install django==1.9.7 (3)使用 pip 查看当前安装的库 cmd> pip show django (4)卸载库 cmd> pip  uninstall django 2.ta

c++中调用python脚本提示 error LNK2001: 无法解析的外部符号 __imp_Py_Initialize等错误的解决方法

最近项目中需要实现一个服务器宕机后短信提醒的功能,个人觉得在使用Python 写http请求这块很方便,发短信这块就使用了python,但是c++程序中调用这个脚本时,编译不通过,提示如下错误: error LNK2001: 无法解析的外部符号 __imp_PyString_FromStringerror LNK2001: 无法解析的外部符号 __imp_PyCallable_Check error LNK2001: 无法解析的外部符号 __imp_PyObject_CallObjecterro