C++开发Python库:解决生成pyd导入pycharm后函数参数名称未定义(为*args和**kwargs)的问题(心路历程)

前言

首先注意:这里的c++开发Python库指的是调用Python C/C++ API,而不是在python里调用dll动态链接库。
最近在研究用c++编写Python库,一顿折腾。
网络上的教程给的例子都是定义一个只接受一个参数的函数,用c++来编写。
我爱折腾,写了一个需要2个参数的c++函数,来让Python调用。
这个模块名称我设置为pure_python,之所以这么命名,是之前研究过用c++的Boost库来开发Python库,现在想研究纯Python原生的开发方式。

工程的基本逻辑

C++函数逻辑很简单:

void run(int a, int b){
	cout << "a:" << a << "\tb:" << "b" << endl; 
}

就是输出a和b的值。

Python中调用:

import pure_python
# 方式1
pure_python.run(1, 2)
# 方式2
pure_python.run(b=2, a=1)

根据网上的教程,转换为Python能接受的开发方式:

#include "pch.h"
#include <Python.h>
#include <iostream>
static PyObject *run(PyObject *self, PyObject *args, PyObject *kwargs)
{
	int a, b;
	// 设置参数列表的名称,如果没有下面这行以及后面那段if语句,则python中不能通过pure_python.run(b=2, a=1)方式来调用
	const char* kwlist[] = { "a", "b", NULL };
	// 利用PyArg_ParseTupleAndKeywords这个API函数,来读取参数列表,把参数列表的值赋值给上面定义的变量
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &a, &b))
	// "ii"的意思是两个变量的类型为int,kwlist为参数名称数组
	{
		return NULL;
	}
	// 执行功能逻辑
	cout << "a:"  << a << "\tb:" << b << endl;
	// 返回python中的None,即相当于啥也不返回
	return Py_None;
	
}
// 定义函数表,也就是定义这个模块有哪些函数,是个结构体数组
// 结构体内{1, 2, 3, 4}。参数1为函数名称,参数2为函数指针,参数3为参数识别方式,参数4为帮助文档,即python用help()显示的内容
// 对于参数3,如果定义函数参数列表是(PyObject *self, PyObject *args, PyObject *kwargs)这样的,必须为METH_VARARGS | METH_KEYWORDS,如果参数列表是(PyObject *self, PyObject *args)这样的,必须为METH_VARARGS,关于其他识别方式,去看官方文档
static PyMethodDef functionInit[] = {
	{"run", (PyCFunction)run, METH_VARARGS | METH_KEYWORDS,"run(a, b) -> None" },
	{NULL, NULL}
};

// 定义模块信息
// 参数1:固定的PyModuleDef_HEAD_INIT,参数2:模块名,与项目名相同,与生成的dll文件相同;参数3:帮助文档。参数4:官方解释是允许的子解释器数量;参数4:函数列表
static PyModuleDef myModule = {
	PyModuleDef_HEAD_INIT,
	"pure_python",
	"不使用BOOST的c++开发模块测试。\n内含有run方法",
	-1,
	functionInit
};

// 初始化模块
// python中import之时首先执行这个方法,相当于__init__.py文件
PyMODINIT_FUNC PyInit_pure_python(void)
{
	return PyModule_Create(&myModule);
}

在vs2019中编译,生成的dll文件后缀名修改为pyd,放到python安装目录的DLLs文件夹下,运行python
在这里插入图片描述
完美。
放到pycharm里。
在这里插入图片描述
等等,这参数列表是怎么回事?
没有a和b这要是给别人用,那岂不是让人n脸懵逼。
之后把参数数量扩展到3个,发现,pycharm里仍然是args, kwargs

分析

python没有参数名的概念,在解释器内,通过解析函数来解析参数列表,解析方式有常用的两种:
1、元组:也就是纯按照位置来定参数,按0、1、2等等等等,分别赋值给定义好的变量,也就是PyArg_ParseTuple这个api
2、字典:按照名称来定参数。这个名称不是你在PyCharm中看到的参数名,也就是之前代码里的PyArg_ParseTupleAndKeywords这个api,我们传入a=1,b=2这对参数后,c++按照定义的kwlist数组内定义的参数名来进行匹配。

所以,我们得出结论:PyCharm中看到的参数名其实是PyCharm软件自身为程序员提供的便利,Python中并没有这个机制。
那么,PyCharm究竟是根据什么来确定出来的呢?

尝试解决

1、我尝试导入python自带的pyd的模块,例如pyexpat,尝试调用里面的方法,发现可以显示参数名。
在这里插入图片描述
故,我决定去python的源代码去寻找答案。结果是令人失望的。我们程序员开发python库利用的是python暴露给我们的api。而python源码内的库并没有利用api进行开发,而是更底层未封装的东西(也可能是用了,但我没能在几千个c文件内找到)。
搁浅1次。

2、尝试去搜度娘,stackflow等论坛,一无所获。
也难怪,这东西出在PyCharm身上。
搁浅2次。

3、尝试分析其他第三方库的源代码。
其他人写的第三方库总不可能不用python C/C++的api吧。这回就拿opencv来开刀。
pip安装opencv-python。
导入PyCharm
在这里插入图片描述
很好,有参数名称。
去翻找源代码。
下载好后用cmake去config和generate,生成vs2019项目文件。具体参见我的另一篇关于编译opencv的文章:添加链接描述

打开后,看着成百上千的cpp文件,手足无措。
笨方法,查找功能找PyArg_ParseTupleAndKeywords这个关键字。
在这里插入图片描述
很好,恰好这个cpp文件就叫cv2。
浏览了一下,这个文件是专门针对python版本的opencv封装,里面全是python的api编程。全都是python可调用的函数。
翻到最后,看看函数列表,模块初始化等等。
在这里插入图片描述
函数列表,函数还挺多的。参数2位置用了自定义的宏
在这里插入图片描述
发现就是封装简化了一下我写的那个代码里的参数2和参数3。我也按照这种方式封装一下,发现问题并没有解决。
那么究竟是什么东西让PyCharm识别出来参数名呢?

思考:
下面我说的关于c++的东西可能存在胡扯。
函数列表存了函数指针,也就是import后函数存到了内存里。PyCharm读到了这些内存,就形成了我们在PyCharm中看到的函数列表自动补全。
我们定义的变量a和b,是存在于函数代码块内。那么PyCharm能识别代码块码?肯定不能啊。编译好的pyd是个二进制文件,哪来的代码块。那opencv里,出现了参数名的又在哪里呢?
经过了一天的思考,对比,翻阅。我发现了。
在函数列表的帮助文档里!!
在这里插入图片描述
如果PyCharm读到了文档,然后正则表达式处理一下。。。好像有道理。

解决

经过冥思苦想,与尝试。
方法如下:定义函数列表时,在函数的帮助文档内写上: 函数名(参数名…) -> 返回值类型

static PyMethodDef functionInit[] = {
	{"run", (PyCFunction)run, METH_VARARGS | METH_KEYWORDS,"run(a, b) -> None" },
	{NULL, NULL}
};
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
首先,你需要安装Boost和Python以及Boost.Python库。然后,按照以下步骤操作: 1. 编写C++代码并使用Boost.Python库将其封装为Python模块。 例如,以下是一个简单的C++代码示例: ```c++ #include <boost/python.hpp> char const* greet() { return "Hello, world!"; } BOOST_PYTHON_MODULE(example) { using namespace boost::python; def("greet", greet); } ``` 这个代码定义了一个名为"greet"的函数,返回一个字符串,然后使用Boost.Python库将其封装为Python模块。 2. 使用CMake创建项目并将其编译为pyd文件。 为了编译生成pyd文件,你需要使用CMake来创建项目,使用Boost和Python的头文件和库文件,并将C++代码编译为pyd文件。 以下是一个CMakeLists.txt文件示例: ```cmake cmake_minimum_required(VERSION 3.0) project(example) find_package(Boost REQUIRED COMPONENTS python) find_package(PythonLibs REQUIRED) include_directories(${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) add_library(example SHARED example.cpp) target_link_libraries(example ${Boost_LIBRARIES} ${PYTHON_LIBRARIES}) set_target_properties(example PROPERTIES SUFFIX ".pyd") ``` 这个文件指定了编译项目所需的Boost和Python库,将C++代码编译为pyd文件,并设置pyd文件的后缀名为".pyd"。 3. 编译并生成pyd文件。 使用以下命令编译项目: ``` cmake . make ``` 这将生成名为"example.pyd"的文件,可以在Windows上使用Python调用。 注意:在Windows上,你需要将Boost库和Python库添加到PATH环境变量中,或者将它们复制到生成pyd文件所在的目录中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值