[개발언어] C언어와 Python의 상호운용성에 대한 자세한 설명

블로거는 개인이나 조직에게 블로거의 원본 기사를 재인쇄하도록 승인하지 않았습니다. 원본에 대한 지원에 감사드립니다!
블로거 링크

저는 국제적으로 유명한 단말기 제조업체에서 근무하며 모뎀 칩 연구 및 개발을 담당하고 있습니다.
5G 초기에는 단말 데이터 비즈니스 레이어와 코어 네트워크 관련 개발 업무를 담당했으며, 현재는 6G 컴퓨팅 네트워크 기술 표준 연구를 주도하고 있다.


블로그 콘텐츠는 주로
       5G/6G 프로토콜 설명,
       컴퓨팅 파워 네트워크 설명(클라우드 컴퓨팅, 엣지 컴퓨팅, 터미널 컴퓨팅),
       고급 C 언어 설명,
       Rust 언어 설명 에 중점을 둡니다.



C 언어와 Python 간의 상호 운용성에 대한 자세한 설명


여기에 이미지 설명을 삽입하세요.

공식 문서 소개: https://docs.python.org/zh-cn/3/extending/index.html

Python은 일부 시스템의 표준 헤더에 영향을 미치는 일부 전처리기 정의를 정의할 수 있으므로 표준 헤더가 포함되기 전에 해당 정의가 포함되어야 합니다 #include<Python.h>. 그리고 항상 Python.h보다 먼저 정의하는 것이 좋습니다 #define PY_SSIZE_T_CLEAN.

1. Python 구현 방법을 호출하는 C 언어

1.1 호출 프로세스

  1. C 언어 데이터를 Python 형식으로 변환합니다.
  2. 변환된 데이터를 사용하여 Python 인터페이스에 대한 함수 호출을 실행합니다.
  3. 호출로 반환된 데이터를 Python에서 C 형식으로 변환합니다.

1.2 주요 구조 및 기능 소개

Python 환경을 초기화하려면 다음 함수를 사용하십시오.

PyConfig_InitPythonConfig()  # 初始化一个PyConfig对象
PyConfig_Read()              # 读取当前环境的配置信息
Py_InitializeFromConfig()    # 使能客制化的Python环境

중요한 구조 다이어그램 중 하나는 PyConfig여러 주요 속성의 의미가 다음과 같다는 것입니다.

  • module_search_paths_set # 다음 변수는 1로 설정된 경우에만 적용됩니다.
  • module_search_paths #지정된 검색 경로 추가

1.3 간단한 Python 문 실행

다음 함수를 사용하여 간단한 Python 문을 실행할 수 있습니다.

# 执行字符串参数中的Python语句
PyRun_SimpleString()

#例如:
PyRun_SimpleString("import sys")

1.4 파일에서 Python 문 실행

다음 함수를 사용하여 파일에서 Python 문을 실행합니다.

# 执行字符串参数中的Python语句
PyRun_SimpleFile()

# 例如:
FILE *fp = fopen("path/to/main.py", "r")
PyRun_SimpleFile(fp, "path/to/main.py")

1.5 Python 모듈 로딩 및 라이브러리 함수 호출

Python 모듈을 로드하고 모듈에서 함수를 호출하는 방법은 다음과 같습니다.

PyImport_ImportModule()    # 加载指定的Python模块
PyObject_GetAttrString()   # 获取模块中的函数或者成员
PyCallable_Check()         # 检测获取的模块对象是否可以调用
PyTuple_New()              # 当从C调用Python函数的时候,入参必须使用元组封装,此函数创建一个元组对象
PyObject_CallObject()      # 调用Python函数

1.6 C 언어 데이터 유형과 Python 데이터 유형 간의 변환

공식 웹사이트 API를 참조하세요: https://docs.python.org/zh-cn/3/c-api/stable.html

요약된 명명 규칙은 다음과 같습니다.

  • Python 데이터 유형을 C 언어 데이터 유형
    Py<Python 유형>_As<C 언어 데이터 유형> 으로 변환
  • C 언어 데이터 유형을 Python 데이터 유형
    Py<Python 유형>_From<C 언어 데이터 유형> 으로 변환

1.7 Python 데이터 객체 생성 및 내장 함수 사용

  • Python에서 데이터 유형을 사용하려면 공식 웹 사이트에서
    Py<Python type>_XXX를 찾을 수 있습니다.
  • Python 내장 기능을 사용하려면
    Py<Python 기본 라이브러리>_XXX를 찾으면 됩니다.

2. Python은 C 언어 구현 방법을 호출합니다.

2.1 호출 프로세스

  1. C 언어 데이터 유형을 Python 형식으로 변환합니다.
  2. 변환된 데이터를 사용하여 Python 인터페이스에 대한 함수 호출을 실행합니다.
  3. 호출로 반환된 데이터를 Python에서 C 언어 형식으로 변환합니다.

2.2 C 함수를 모듈로 패키징

C 변수와 메소드를 클래스로 캡슐화한 다음(모듈 수준 메소드를 정의할 수도 있음), 이를 모듈로 패키징하고 게시해야 합니다.그러면 Python은 C 함수를 사용할 수 있습니다. 두 가지 주요 데이터 구조가 아래에 소개됩니다.

  • PyModuleDef
    여기에 이미지 설명을 삽입하세요.

    • m_base : 기본 클래스이며, 항상 PyModuleDef_HEAD_INIT여야 합니다.
    • m_name : 모듈 이름
    • m_size : 현재 -1로 설정되어 있으며 일부 고급 사용법에서는 이 매개변수를 사용합니다.
    • m_methods : 모듈 메소드 목록
  • PyMethodDef
    여기에 이미지 설명을 삽입하세요.

    • ml_name : Python에서 표시되는 메서드 이름
    • ml_meth : 해당 C 함수 이름
    • ml_flags : 함수에 매개변수가 있는지 여부를 나타냅니다.

2.3 클래스를 정의하는 방법

클래스를 정의하는 주요 데이터 유형은 PyTypeObject일부 클래스 속성이 이 유형으로 정의된다는 것입니다.

  • tp_name : 클래스 이름(형식은 modulename.classname)
  • tp_basicsize : 공간을 할당하는 데 사용되는 클래스의 크기
  • tp_itemsize : 정적 클래스인 경우 0, 동적 클래스인 경우 0이 아닙니다.
  • tp_flags : 클래스의 속성 매개변수는 최소한 Py_TPFLAGS_DEFAULT 여야 합니다.
  • tp_new : 클래스의 인스턴스화 함수
  • tp_init : 클래스의 초기화 프로그램
  • tp_dealloc : 클래스의 소멸자
  • tp_members : 회원 목록
  • tp_methods : 메소드 목록 (구조는 모듈과 동일)
  • tp_getset : 속성 가져오기 및 설정 함수

멤버 정의를 포함하는 구조 PyMemberDef, 핵심 멤버의 의미:

  • name : Python에 표시되는 멤버 이름
  • 유형 : 회원 유형
  • offset : 구조체에 있는 멤버의 오프셋입니다. offset() 함수를 사용하여 구합니다.

속성의 get 및 set 메소드 구조 PyGetSetDef와 주요 멤버의 의미를 정의합니다.

  • name : Python에서 표시되는 속성의 이름
  • get , set : 해당 속성의 get 및 set 메소드

2.4 모듈의 주요 기능 정의

우리가 정의한 모듈이 Python에서 호출되면 PyMODINIT_FUNC PyInit_<moduleName>(void)함수가 호출됩니다. 간단한 PyInit_(void) 구현 프로세스는 다음과 같습니다:

  • PyType_Ready()우리를 위해 정의된 정적 클래스를 사용하여 메모리 공간을 분류합니다.
  • PyModule_Create()생성 모듈을 사용하십시오 .
  • 그런 다음 PyModule_AddObject()우리가 정의한 클래스를 모듈에 등록하는 데 사용합니다.

자세한 과정은 아래 데모를 참고해주세요

2.5 설정 스크립트를 작성하고 C 언어를 so, pyd 및 기타 형식으로 컴파일

# file name 'setup.py'

from distutils.core import setup, Extension

module1 = Extension('moduleName', sources = ['moduleName.c'])

setup (name = 'moduleName'
		version = '1.0'
		description = 'This is a Demo'
		ext_modules = [module1 ])

위 코드의 moduleName을 자신의 모듈 이름으로 바꾸고 해당 C 파일을 소스에 추가합니다. 헤더 파일을 추가할 필요가 없습니다. 다음 명령을 사용하여 컴파일하고 설치합니다.

python setup.py build

python setup.py install

물론 C 언어를 호출하는 Python을 구현하는 라이브러리가 많이 있습니다. 예를 들어

  • 사이썬
  • cffi
  • ctypes
  • 통음

3. C언어와 Python의 상호운용성 예시

3.1 C 언어 호출 Python

데모.py 파일

def print_c_point(p)
	print(p)

main.c 파일

#define PY_SSIZE_T_CLEAN
#include <Python.h>

PyStatus init_python(const char *program_name, const wchar_t *additional_search_path)
{
    
    
	assert(program_name);
	
	PyStatus status;
	PyConfig config;
	PyConfig_InitPythonConfig(&config);

	status = PyConfig_SetBytesString(&config, &config.program_name, program_name);
	if(PyStatus_Exception(status)){
    
    
		goto done;
	}

	status = PyConfig_Read(&config)
	if(PyStatus_Exception(status)){
    
    
		goto done;
	}

	if(additional_search_path){
    
    
		config.module_search_paths_set = 1;
		status = PyWideStringList_Append(&config.module_search_paths, additional_search_path);
		if(PyStatus_Exception(status)){
    
    
			goto done;
		}
	}

	status = Py_InitializeFromConfig(&config);

done:
	PyConfig_Clear(&config);
	return status;
}

int main(int argc, char *argv[])
{
    
    
	init_python(argv[0], NULL);

	PyRun_SimpleString("from time import time, ctime\n"
						"print('Today is', ctime(time()))\n");

	File *fp = fopen("path/to/demo.py", "r");
	PyRun_SimpleFile(fp, "path/to/demo.py");

	PyObject *pyModule, *pyFunc;
	PyObject *pyArgs, *pyValue;
	
	pyModule = PyImport_ImportModule(demo.py);
	if(!pyModule){
    
    
		PyErr_Print();
		goto end;
	}
	
	pyFunc = PyObject_GetAttrString(pyModule, print_c_point);
	if(!pyFunc){
    
    
		Py_DECREF(pyModule);
		PyErr_Print();
		goto end;
	}

	if(PyCallable_Check(pyFunc)){
    
    
		pyArgs = PyTuple_New(1);

		for(int i=0;i < 1;++i){
    
    
			pyValue = PyLong_FromLong(3);
			if(!pyValue){
    
    
				Py_DECREF(pyArgs);
				PyErr_Print();
				goto end;
			}

			PyTuple_SetItem(pyArgs, i, pyValue);
		}

		pyValue = PyObject_CallObject(pyFunc, pyArgs);
		Py_DECREF(pyArgs);
		if(pyValue){
    
    
			printf("The result is %ld.\n". PyLong_AsLong(pyValue));
			Py_DECREF(pyValue);
		} else {
    
    
			PyErr_Print();
			goto end;
		}
	}

	Py_DECREF(pyFunc);
	Py_DECREF(pyModule);

end:
	if(Py_FinalizeEx() < 0)
		exit(-1);

	return 0;
}


3.2 파이썬은 C 언어를 호출한다

main.py 파일

import custom

if '__main__' == __name__:
	use_custom("custom module", 1234)

custom.c 파일

typedef struct {
    
    
	PyObject_HEAD
	PyObject *user_name;
	unsigned int passwd;
} customObject;


static int 
custom_clear(customObject *self)
{
    
    
	Py_CLEAR(self->user_name);

	return 0;
}

static void
custom_dealloc(customObject *self)
{
    
    
	PyObecjt_GC_UnTrack(self);
	custom_clear(self);
	Py_TYPE(self)->tp_free((PyObject*) self);
}

static PyObject*
custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    
    
	customObject *self;
	self = (customObject *)type->tp_alloc(type, 0);
	if(self != NULL){
    
    
		self->user_name = PyUnicode_FromString("");
		if(self->user_name == NULL){
    
    
			Py_DECREF(self);
			return NULL;
		}

		self->passwd = 1234;
	}

	return (PyObject *) self;
}

static int
custom_init(customObject *self, PyObject *args, PyObject *kwds)
{
    
    
	static char *kwlist[] = {
    
    "user_name","passwd",NULL};
	PyObject *user_name = NULL, *tmp;
	if(!PyArg_ParseTupleAndKeywords(args, kwds, "|UkI", kwlist, &user_name, &self->passwd))
		return -1;

	if(user_name){
    
    
		tmp = self->user_name;
		Py_INCREF(user_name);
		self->user_name = user_name;
		Py_DECREF(tmp);
	}
	
	return 0;
}

static PyMemberDef 
custom_members[] = {
    
    
	{
    
    "passwd", T_ULONG, offset(customObject, passwd), 0, "user password"},
	{
    
    NULL}
};

static PyObject *
custom_getusername(customObject *self, void *closure)
{
    
    
	Py_INCREF(self->user_name);
	return self->user_name;
}

static PyObject *
custom_setusername(customObject *self, PyObject *value, void *closure)
{
    
    
	if(value == NULL) {
    
    
		PyErr_SetString(PyExc_TypeError, "user name is not NULL");
		return -1;
	}

	if(!PyUnicode_Check(value)) {
    
    
		PyErr_SetString(PyExc_TypeError, "user name should string");
		return -1;
	}
	
	Py_INCREF(value);
	Py_CLEAR(self->user_name);
	self->user_name = value;

	return 0;
}

static int
custom_getpassword(customObject *self, void *closure)
{
    
    
	PyObject *tmp = PyLong_FromUnsignedLong(self->passwd);
	Py_INCREF(tmp);
	
	return tmp;
}

static int
custom_setpassword(customObject *self, PyObject *value, void *closure)
{
    
    
	if(value == NULL) {
    
    
		PyErr_SetString(PyExc_TypeError, "user password is not NULL");
		return -1;
	}

	if(!PyLong_Check(value)) {
    
    
		PyErr_SetString(PyExc_TypeError, "user password should integer");
		return -1;
	}

	self->passwd = PyLong_AsUnsignedLong(value);

	return 0;
}

static PyGetSetDef
custom_getsetters[] = {
    
    
	{
    
    "user_name", (getter)custom_getusername, (setter)custom_setusername, "user name", NULL},
	{
    
    "passwd", (getter)custom_getpassword, (setter)custom_setpassword, "user password", NULL},
	{
    
    NULL}
};

static PyObject*
custom_printUserInfo(customObject *self, PyObject *Py_UNUSED(ignored))
{
    
    
	printf("user name is %s and password is %ld.\n",self->user_name,self->passwd);
}

static PyMethodDef custom_methods[] = {
    
    
	{
    
    "custom_printUserInfo", (PyCFunction) custom_printUserInfo, METH_NOARGS, "print user info"},
	{
    
    NULL}
};


static PyTypeObject customType = {
    
    
	PyVarObject_HEAD_INIT(NULL,0)
	.tp_name = "custom.custom",
	.tp_doc = PyDoc_STR("custom object"),
	.tp_basicsize = sizeof(customObject),
	.tp_itemsize = 0,
	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
	.tp_new = custom_new,
	.tp_init = (initproc)custom_init,
	.tp_dealloc = (destructor) custom_dealloc,
	.tp_clear = (inquiry) custom_clear,
	.tp_members = custom_members,
	.tp_methods = custom_methods,
	.tp_getset = custom_getsetters,
};

static PyModuleDef custommodule = {
    
    
	PyModuleDef_HEAD_INIT,
	.m_name = "custom",
	.m_doc = "example module that creates an extension type",
	.m_size = 1
};

PyMODINIT_FUNC
PyInit_custom(void)
{
    
    
	PyObject *m;

	if(PyType_Ready(&customType) < 0)
		return NULL;

	m = PyModule_Create(&custommodule);
	if(m == NULL) return NULL;

	Py_INCREF(&customType);
	if(PyModule_AddObject(m, "custom", (PyObject*)&customType) < 0){
    
    
		Py_DECREF(&customType);
		Py_DECREF(m);
		return NULL;
	}

	return m;
}




여기에 이미지 설명을 삽입하세요.

Supongo que te gusta

Origin blog.csdn.net/qq_31985307/article/details/132642686
Recomendado
Clasificación