Python3调用C程序(超详解)

Python76

Python3调用C程序(超详解)

Python为什么要调用C?

1.要提高代码的运算速度,C比Python快50倍以上

2.对于C语言里很多传统类库,不想用Python重写,想对从内存到文件接口这样的底层资源进行访问

Python调用C的方法:

Python调用C的方法通常有3种:

1.SWIG,编写一个额外的接口文件来作为SWIG(终端工具)的入口

2.通过CTypes调用

3.使用Python/C API方法

第一种方法大多数情况下会带来不必要的麻烦,我并没有试验,本文只针对2,3方法作详细说明

通过CTypes调用:

Python中的ctypes模块可能是Python调用C方法中最简单的一种。ctypes模块提供了和C语言兼容的数据类型和函数来加载dll文件,因此在调用时不需对源文件做任何的修改。也正是如此奠定了这种方法的简单性。

下面是python文件的代码:

from ctypes import *    #pip ctypes库,并导入库
test = CDLL("./test.dll")   #调用当前目录下叫test的dll文件,dll文件是C生成的动态链接库
result =test.sum(5,10)  #调用库里的函数sum,求和函数
print(result)   #打印结果

接下来用C语言编写dll动态链接库,这里使用VS:

Python3调用C程序(超详解)
Python3调用C程序(超详解)

单击头文件,新建项:

Python3调用C程序(超详解)

添加源文件:

Python3调用C程序(超详解)

在头文件test.h中加入如下代码:

#pragma once

#ifdef BUILD_TEST
#define API_SYMBOL __declspec(dllexport)
#else
#define API_SYMBOL __declspec(dllimport)
#endif
//宏定义,导出或者导入//

extern "C" API_SYMBOL int sum(int x, int y);
//导出函数//

在源文件test.cpp中加入如下代码:

#define BUILD_TEST  //使导出函数生效
#include "test.h"
#include "stdio.h"
#include "pch.h"

int sum(int a, int b) {
    return a + b;
}

注意,这里要点击64位,再点击生成(因为目前大部分电脑安装Python解释器是64位的,否则默认生成32位的动态库,会导致无法调用) 这是一个很隐蔽的坑!!!

Python3调用C程序(超详解)

在生成的动态库路径下找到test.dll文件,并复制到python项目下

Python3调用C程序(超详解)

Python3调用C程序(超详解)

最后在python中运行代码,出现如下问题:

Python3调用C程序(超详解)

为了寻找问题,我将python文件中的代码替换如下,发现调用动态库是成功的,只是不能调用动态库里面的函数

from ctypes import *
from ctypes import *    #pip ctypes库,并导入库
test = CDLL("./test.dll")   #调用当前目录下叫test的dll文件,dll文件是C生成的动态链接库

print("加载成功")

# result =test.sum(5,10)    #调用库里的函数sum,求和函数
# print(result) #打印结果

Python3调用C程序(超详解)

以上流程我是参考B站一位UP主的具体教学,但还是行不通,于是我将test.cpp源文件中的代码更改如下:

#define BUILD_TEST
#include "test.h"
#include "stdio.h"
#include "pch.h"

#define DLLEXPORT extern "C" __declspec(dllexport)  //直接在源文件定义导出

DLLEXPORT int sum(int a, int b) {
    return a + b;
}//两数相加

重复上述流程,生成dll文件,将文件放置于python项目中,然后调用,终于成功

from ctypes import *
from ctypes import *    #pip ctypes库,并导入库
test = CDLL("./test.dll")   #调用当前目录下叫test的dll文件,dll文件是C生成的动态链接库

print("加载成功")

result =test.sum(5,10)  #调用库里的函数sum,求和函数
print(result)   #打印结果

Python3调用C程序(超详解)

使用Python/C API方法:

Python/C API可能是被最广泛使用的方法。它不仅简单,而且可以在C代码中操作你的Python对象。但要使用这种方法,需要用特定的方式来编写C代码,所以C代码不是原生的C(大伙要适应一下),这样才可以供python去调用。这里参照python进阶的说明博客园的一篇文章

python文件的代码如下:

import Test
x = 1
print(Test.add_one(x))

而这里的Test模块,则是需要我们自己用C语言写,C文件代码如下:

#include

int add_one(int a){
    return a + 1;
}//这是C的原生函数,实现+1功能

static PyObject * py_add_one(PyObject *self, PyObject *args){
    int num;
    if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
    return PyLong_FromLong(add_one(num));
}//这一串代码要实现的功能如下,按照python规定的调用方式:
//1.定义一个新的静态函数,接收2个PyObject *参数,返回1个PyObject *值
//2.PyArg_ParseTuple方法将python输入的变量变成C的变量,即上述args→num
//3.紧接着调用C原生函数add_one,传入num
//4.最后将调用返回的C变量,转换为PyObject*或其子类,并通过PyLong_FromLong方法,返回python值

static PyMethodDef Methods[] = {
    {"add_one", py_add_one, METH_VARARGS},
    {NULL, NULL}
};//这串代码的目的是创建一个数组,来指明Python可以调用这个扩展函数:
//其中"add_one",代表编译后python调用时希望使用的函数名。而py_add_one,代表要调用当前C代码中的哪一个函数,即static PyObject * py_add_one。METH_VARARGS,代表函数的参数传递形式,主要包括位置参数和关键字参数两种
//最后如果希望添加新的函数,则在最后的{NULL, NULL}里按同样格式填写新的调用信息,比如加一些求和求乘积函数

static struct PyModuleDef cModule = {
    PyModuleDef_HEAD_INIT,
    "Test", /*模块名,即是生成模块后的名字,如numpy,opencv等等*/
    "", /* 模块文档,可以为空NULL */
    -1, /* 模块中每个解释器的状态大小,模块在全局变量中保持状态,则为-1 */
    Methods//即上一步定义的Methods,说明了可调用的函数集
};//创建module的信息

PyMODINIT_FUNC PyInit_Test(void){ return PyModule_Create(&cModule);}
//module初始化

将C文件放置在python项目的同文件目录下,然后编写setup.py文件,setup.py是用来将C打包成模块的脚本文件,代码如下:

from distutils.core import setup, Extension #这里要用到distutils库
module1 = Extension('Test', sources = ['test.c']) #打包文件为test.c,这里没有设置路径,所有.c文件和setup.py放在同一目录下
setup (name = 'Test', #打包名
       version = '1.0', #版本
       description = 'This is a demo package', #说明文字
       ext_modules = [module1])

然后在pycharm的Terminal终端输入(因为这里用的python3.5+,windows平台下python的C/C++扩展不再支持gcc的编译,并强制要求使用msvc进行编译,如果版本低于3.5,用 python setup.py build这是一个隐藏的坑!!!用python setup.py build一直出问题

python setup.py build --compiler msvc #编译代码
python setup.py build --compiler msvc install #编译代码并直接将包放入当前python环境的包的路径以供调用

Python3调用C程序(超详解)

Python3调用C程序(超详解)

最后结果,顺利打包,我们在python里安装了自己的包并完成调用:

Python3调用C程序(超详解)

Python3调用C程序(超详解)

Python3调用C程序(超详解)

Original: https://www.cnblogs.com/tuaisen/p/15574190.html
Author: 图爱森
Title: Python3调用C程序(超详解)



相关阅读

Title: 一文让你掌握Python面向对象的编程思维

一、类和对象

1、万物皆对象

2、对象:用来描述客观事物的一个 实体 ,由一组 属性 和 方法 构成

3、属性:对象具有的各种特征,每个对象的每个属性都有特定值

4、方法:对象执行的操作

5、类:对象的分类,分类就是认识对象的过程

很多人学习蟒蛇,不知道从何学起。

很多人学习寻找python,掌握了基本语法之后,不知道在哪里案例上手。

很多已经可能知道案例的人,却不怎么去学习更多高深的知识。

这三类人,我给大家提供一个好的学习平台,免费获取视频教程,电子书,以及课程的源代码!

QQ群:101677771

欢迎加入,一起讨论学习

面向对象设计的规则

1、图书类: 属性:图书编号、书名、书的作者、上架日期、借出状态

功能(方法):借出和归还

2、银行卡类: 属性:卡号、银行卡名称、密码、姓名、余额

方法:登入、存款、取款

二、面向对象创建:定义某个类的对象具有的属性(变量)和方法(函数)

三、面向对象思想:封装、继承、多态

1、封装(属性私有化)

  • 私有属性可以被查看,但是不能被修改 ---> get方法

  • 私有属性不被外界查看,但可以被外界修改 ---> set方法】

<span class="hljs-string">""<span class="hljs-string">"&#x5C01;&#x88C5;&#xFF08;&#x5C5E;&#x6027;&#x79C1;&#x6709;&#x5316;&#xFF09;"<span class="hljs-string">""

<span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">Student:
    <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">__init__<span class="hljs-params">(<span class="hljs-keyword">self, name, age, xh, id, address):
        <span class="hljs-keyword">self.name = name  </span></span></span></span></span></span></span></span></span></span></span></span>

2、继承、多态

  • 继承在父类基础上构造出新的类,子类可以拥有继承**父类的属性和方法
  • 多态:指一个对象可以用子类的特点也可以父类的特征
<span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">Animal:
    <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">__init__<span class="hljs-params">(self, name, color):
        self.name = name
        self.color = color

    <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">eat<span class="hljs-params">(self, food):
        print(<span class="hljs-string">f'<span class="hljs-subst">{self.name}&#x5728;&#x5403;<span class="hljs-subst">{food}')

    <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">drink<span class="hljs-params">(self, sthing):
        print(<span class="hljs-string">f'<span class="hljs-subst">{self.name}&#x5728;&#x559D;<span class="hljs-subst">{sthing}')

    <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">play<span class="hljs-params">(self):
        print(<span class="hljs-string">f'<span class="hljs-subst">{self.name}&#x5728;&#x73A9;')

    <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">sleep<span class="hljs-params">(self):
        print(<span class="hljs-string">f'<span class="hljs-subst">{self.name}&#x5728;&#x7761;&#x89C9;')

</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>

Original: https://www.cnblogs.com/sn5200/p/15826467.html
Author: Python可乐的呀
Title: 一文让你掌握Python面向对象的编程思维