Python 3 calls C program (super detailed explanation)

Python 3 calls C program (super detailed explanation)

Why does Python call C?

1. To improve the operation speed of code, C is more than 50 times faster than Python

2. For many traditional class libraries in C language, I don't want to rewrite them in Python. I want to access the underlying resources from memory to file interface

Python calls C's method:

Python usually calls C in three ways:

1.SWIG, write an additional interface file as the entry of swig (terminal tool)

2. Call through CTypes

3. Use Python/C API method

The first method will bring unnecessary trouble in most cases. I have not tested it. This paper only describes methods 2 and 3 in detail

Call via CTypes:

The ctypes module in Python is probably the simplest way Python calls C methods. Ctypes module provides data types and functions compatible with C language to load dll files, so there is no need to modify the source file when calling. It is precisely in this way that the simplicity of this method is established.

Here is the code for the python file:

from ctypes import *    #pip ctypes library, and import to warehouse
test = CDLL("./test.dll")    #Call the dll file called test in the current directory. The dll file is the dynamic link library generated by C
result =test.sum(5,10)    #Call the sum function in the library
print(result)    #Print results

Next, write dll Dynamic Link Library in C language. Here VS is used:

Click header file to create a new item:

Add source file:

Add the following code to the header file test.h:

#pragma once

#ifdef BUILD_TEST
#define API_SYMBOL __declspec(dllexport)
#else
#define API_SYMBOL __declspec(dllimport)
#endif
//Macro definition, export or import//

extern "C" API_SYMBOL int sum(int x, int y);
//Export function//

Add the following code to the source file test.cpp:

#define BUILD_TEST / / make the export function effective
#include "test.h"
#include "stdio.h"
#include "pch.h"

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

Note that click 64 bit here and then click generate (because most computers currently have 64 bit Python interpreters installed, otherwise 32-bit dynamic libraries will be generated by default, which will lead to failure to call). This is a very hidden pit!!!

Find the test.dll file in the generated dynamic library path and copy it to the python project

Finally, when running the code in python, the following problems occur:

In order to find the problem, I replaced the code in the python file as follows. I found that calling the dynamic library is successful, but I can't call the functions in the dynamic library

from ctypes import *
from ctypes import *    #pip ctypes library, and import to warehouse
test = CDLL("./test.dll")    #Call the dll file called test in the current directory. The dll file is the dynamic link library generated by C

print("Loading succeeded")

# result =test.sum(5,10)    #Call the sum function in the library
# print(result)    #Print results

The above process is for my reference Specific teaching of one UP master in station B , but it still didn't work, so I changed the code in the test.cpp source file as follows:

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

#define DLLEXPORT extern "C" __declspec(dllexport) / / define and export directly in the source file

DLLEXPORT int sum(int a, int b) {
    return a + b;
}//Add two numbers

Repeat the above process, generate dll files, place files in python project, then call, and finally succeed.

from ctypes import *
from ctypes import *    #pip ctypes library, and import to warehouse
test = CDLL("./test.dll")    #Call the dll file called test in the current directory. The dll file is the dynamic link library generated by C

print("Loading succeeded")

result =test.sum(5,10)    #Call the sum function in the library
print(result)    #Print results

Using Python/C API methods:

The Python/C API is probably the most widely used method. It is not only simple, but also can manipulate your Python objects in C code. But to use this method, you need to write c code in a specific way, so c code is not native C (everyone should adapt), so it can be called by python. Reference here Description of advanced python and An article in the blog Garden

The code of python file is as follows:

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

The Test module here needs to be written in C language. The C file code is as follows:

#include <Python.h>

int add_one(int a){
    return a + 1;      
}//This is the native function of C to realize the + 1 function

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));
}//The functions to be realized by this string of code are as follows, according to the calling method specified by python:
//1. Define a new static function, receive 2 PyObject * parameters and return 1 PyObject * value
//2.PyArg_ The parsetuple method changes the variable input by python into a variable of C, that is, args → num above
//3. Then call the C native function add_one, pass in num
//4. Finally, convert the C variable returned by the call to PyObject * or its subclass, and pass PyLong_FromLong method, return python value

static PyMethodDef Methods[] = {
    {"add_one", py_add_one, METH_VARARGS}, 
    {NULL, NULL}
};//The purpose of this string of code is to create an array to indicate that Python can call this extension function:
//Where "add_one" represents the function name you want to use when calling python after compilation. And py_add_one represents which function in the current C code to call, that is, static PyObject * py_add_one.  METH_VARARGS represents the parameter transfer form of a function, mainly including position parameters and keyword parameters
//Finally, if you want to add a new function, fill in the new call information in the same format in the last {NULL, NULL}, such as adding some sum and product functions

static struct PyModuleDef cModule = {
    PyModuleDef_HEAD_INIT,
    "Test", /*Module name, that is, the name of the generated module, such as numpy,opencv, etc*/
    "", /* Module document, which can be empty or NULL */
    -1, /* The state size of each interpreter in the module. If the module maintains the state in the global variable, it is - 1 */
    Methods//The Methods defined in the previous step describe the callable function set
};//Create module information

PyMODINIT_FUNC PyInit_Test(void){ return PyModule_Create(&cModule);}
//module initialization

Place the C file in the same file directory of the python project, and then write the setup.py file. Setup.py is a script file used to package C into modules. The code is as follows:

from distutils.core import setup, Extension #The distutils library is used here
module1 = Extension('Test', sources = ['test.c']) #The package file is test.c. There is no path set here. All. c files and setup.py are placed in the same directory
setup (name = 'Test', #Package name
       version = '1.0', #edition
       description = 'This is a demo package', #Descriptive text
       ext_modules = [module1])

Then input in the Terminal terminal of pychart (because python 3.5 + is used here, the C/C + + extension of python under windows platform no longer supports gcc compilation, and msvc is mandatory for compilation. If the version is lower than 3.5, use python setup.py build). This is a hidden pit!!! There is always a problem with python setup.py build:

python setup.py build --compiler msvc #Compile code
python setup.py build --compiler msvc install #Compile the code and put the package directly into the path of the package in the current python environment for invocation

Finally, the package was successfully packaged. We installed our own package in python and completed the call:

This article is composed of blog one article multi posting platform OpenWrite release!

Tags: Python

Posted on Thu, 18 Nov 2021 06:47:04 -0500 by amazing