Python与C的交互(一)

Python与C的交互(一)

编者使用的是手机编辑此文档,因为一些未知原因,一次不能编写太久,否则无法发表。因此本系列文章目前还未完成,但将持续更新。

编者由于临近期末考试,还要背各科资料,因此version 7的更新将于7月2日前发布,请见谅。
(虽然以编者的水平写出来的文章,估计也没几个人看,不过还是要认真对待)

本文更新日志:
2021.06.11 10:20 发布
2021.06.11 11:37 更新 version 2
2021.06.12 10:52 更新 version 3
2021.06.12 11:26 更新 version 4
2021.06.13 16:19 更新 version 5
2021.06.19 23:24 更新 version 6

预计将在2021.07.02日前发布下一更新。

(1st) | CPython扩展(一)

CPython API

首先要下载CPython(注意不是Cython)的源码:Python官网
下载好之后,可以看到里面有很多文件夹。我们要用的API在Include/Python.h里面。

启动Python虚拟机并配置搜索路径

先上一段代码:

#include "cpython/Include/Python.h"
#include <stdio.h>

int main(int argc, char** argv)
{
    Py_Initialize();
    if (Py_isInitialized() == 0){
        printf("Python虚拟机启动失败");
        return 1;
    } else {
        printf("Python虚拟机启动成功");
    }

    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('C:/Users/Administrator/AppData/Local/Programs/Python/Python37-32/Libs/')");
    // 这个路径是Python模块的放置路径

    // TODO: 对Python的操作
    // ...

    Py_Finalize();  // 关闭虚拟机
    return 0;
}

下面我们来看一下这段代码。

代码中出现的新内容
函数 void Py_Initialize(): 用于启动Python虚拟机
函数 bool Py_isInitialized(): 用于判断Python虚拟机是否成功启动。成功则返回true,否则返回false
函数 PyRun_SimpleString(const char* c): 用于执行单行Python语句。(这个函数的原形记不得了,抱歉)
函数 void Py_Finalize(): 用于关闭Python虚拟机。

这段代码首先调用了Py_Initialize函数启动虚拟机,然后通过if语句和Py_isInitialized函数确认虚拟机是否开启成功,如果开启失败则退出程序。然后通过Python的sys模块,执行Python代码,将Python模块路径加入到Python的模块搜索路径列表sys.path中去。然后关闭虚拟机。

PyObject结构体指针

object.h头文件中,定义了一个结构体PyObject所有Python对象都可以用PyObject结构体来表示
请看如下C代码:

#include "cpython/Include/Python.h"

int main(int argc, char** argv)
{
    Py_Initialize();
    if (Py_isInitialized() == 0){return 1;}

    PyObject* integer = PyInt_FromLong(4028);

    Py_Finalize();
    return 0;
}

代码中出现的新内容
结构体 PyObject: 包含了Python对象的引用计数(用于垃圾回收)、类型等。
函数 PyObject* PyInt_FromLong(long arg1): 通过long类型创建一个带int类型的PyObject结构体指针。

其实这段代码并没有什么实际用处,只是作为了解PyObject这一节的代码片段而已。(这是编者的失误)

编写简单的Python扩展

Python扩展最后会编译为.so文件或.pyd文件(实质上就是动态链接库,在Windows下把.pyd重命名为.dll也可以给其它程序调用)。在Python中,.so、.pyd、.dll都可以作为Python模块,直接使用import关键字或__import__函数导入。1
请看这段C代码:

#include "cpython/Include/Python.h"
#include "stdio.h"

int main(int argc, char** argv)
{
    Py_Initialize();
    if (Py_isInitialized() == 0){return 1;}
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('C:/.../Python37-32/Lib')");

    PyObject* mod =  PyImport_ImportModule("os");
    PyObject* dict = PyDict_GetDictFromModule(mod);
    PyObject* getcwd = PyDict_GetItem(dict, "getcwd");
    PyObject* args = PyTuple_New(0);
    PyObject* ret = PyObject_CallObject(getcwd, args);
    const char* cwd = PyStr_AsString(ret);
    printf(cwd);

    Py_Finalize();
}

代码中新出现的内容
函数 PyObject* PyImport_ImportModule(const char* mod): 接受一个字符串参数(模块名),返回模块的PyObject*对象。
函数 PyObject* PyDict_GetDictFromModule(PyObject* mod): **(不知道现在是否已经弃用)**获取模块中的Dict(可理解为Python代码中的__dict__)。
函数 PyObject* PyDict_GetItem(PyObject* dict, const char* item): 从Python字典中获取对象。
函数 PyObject* PyTuple_New(int l): 接受一个int类型参数作为长度,返回一个元组(tuple)。这里tuple用来作为调用函数的参数集。
函数 PyObject* PyObject_CallObject(PyObject* callable, PyObject* args): 调用一个Python对象。第一个参数是被调用对象,第二个参数是tuple(作为参数集)。
函数 const char* PyStr_AsString(PyObject* string): 将一个str类型的Python对象转化为const char*类型字符串。

这段代码首先导入了os模块,然后用dict作为“中介”,获取了可调用对象getcwd函数,再调用这个函数获取当前运行路径,最后将其转化为const char*类型输出。

(2nd) | C调用Python

int对象

[选修] int的C原形2

int在Python源代码的intobject.h头文件中定义为一个结构体,原形如下:

// intobject.h
typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

在这里面,PyObject_HEAD宏指明了几乎每个内置对象都具有的内容,包括引用计数等;ob_ival是长整数型,代表这个PyIntObject对象所维护的实际数值。

我们知道,Python对象的元信息都保存在其对应的类型中。下面来看一看PyInt_Type

// intobject.c
PyTypeObject PyInt_Type = {
    PyObject_HEAD_INIT(&PyType_Type)
    0, 
    "int",
    sizeof(PyIntObject), 
    0,
    (destructor) int_dealloc, /* tp_dealloc */
    (printfunc) int_print,    /* tp_print */
    0,                        /* tp_getattr */
    0,                        /* tp_setattr */
    (cmpfunc) int_compare,    /* tp_compare */
    (reprfunc) int_repr,      /* tp_repr */
    &int_as_number,           /* tp_as_number */
    0,                        /* tp_as_sequence */
    0,                        /* tp_as_mapping */
    (hashfunc) int_hash,      /* tp_hash */
    0,                        /* tp_call */
    (reprfunc) int_repr,      /* tp_str */
    PyObject_GenericGetAttr,  /* tp_getattro */
    0,                        /* tp_setattro */
    0,                        /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */
    int_doc,                  /* tp_doc */
    0,
    0,
    0,
    0,
    0,
    0,
    int_methods,              /* tp_methods */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    int_new,                  /* tp_new */
    (freefunc) int_free,      /* tp_free */
};

基于上述二者,我们就可以执行一些算术操作。例如比较两个int对象:

// intobject.c
static int int_compare(PyObject *v, PyObject *w)
{
    register long i = v->ival;
    register long j = w->ival;
    return (i < j) ? -1 : (i > j) ? 1 : 0;
}

这就是int的C原形了。

int类型相关函数

Updating …

编者技术有限,如有不足敬请指教!


  1. .dll〕在Python 2.x中可以作为模块导入,但在Python 3.x中废弃,只能重命名为.pyd再导入。 ↩︎

  2. int的C原形〕本小节的CPython源码摘自《Python源码剖析》,陈儒著,电子工业出版社出版。在此表示衷心的感谢。 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值