【開発言語】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. C言語呼び出しPython実装方法

1.1 通話プロセス

  1. C 言語データを Python 形式に変換します。
  2. 変換されたデータを使用して、Python インターフェイスへの関数呼び出しを実行します。
  3. 呼び出しによって返されたデータを Python から C 形式に変換します。

1.2 主要な構造と機能の概要

次の関数を使用して、Python 環境を初期化します。

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

重要な構造図の 1 つは、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のデータ型間の変換

公式 Web サイトの 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 でデータ型を使用したい場合は、公式 Web サイトで
    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 関数を使用できるようになります。2 つの主要なデータ構造を以下に紹介します。

  • 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 で認識される属性の名前
  • getset : 対応する属性の 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 を実装するライブラリが多数あります。

  • サイソン
  • ちふぃ
  • 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言語を呼び出すPython

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;
}




ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/qq_31985307/article/details/132642686