gen

    在import一个so库里的类或函数时,有时发现so文件分明就在那路径下,可是总是报错ModuleNotFoundError: No module named  ***,这种错误的可能原因有:

1.首先要确保so所在的路径已经包含在sys.path里了,如果so所在目录已经是在python默认的系统路径里,例如/usr/lib/python3.6/dist-packages/或者/usr/local/lib/python3.6/dist-packages/之下的任何层级的目录,不用做任何设置,如果是其他路径,可以通过设置PYTHONPATH或者程序里使用sys.path.insert()或sys.path.append()把路径添加到sys.path里来。

2.路径包含正确了,检查so库的命名的前缀和import是否不一致,这种so库的命名是有一定规则的,例如,Linux上一般是<so_name>.cpython-<python-version>-<cpu-platform>-linux-gnu.so,在import时指定的名字需要和<so_name>保持一致。

3.命名正确了,检查后面的后缀cpython-<python-version>-<cpu-platform>-linux-gnu.so是否在你当前使用的python版本的支持范围内,例如,你的so库是python3的,可你在误操作下在使用python2运行程序,或者你的so库是针对python3.5的(so后缀里的python-version=35m),但你当前使用的是python3.6,也不行,所以如果不是在只安装了一种版本的python的环境里,运行python程序前,最好检查一下python版本是否是你所希望的,这个是在安装了多个版本的python的环境下或者升级了python版本后经常不经意下易犯的错误,有时还被坑浪费很多时间查找原因,除了so库分明在那里却总是报ModuleNotFoundError,还有其他七七八八奇怪的错误,查找原因最后发现是python版本用错了,气得血冲脑门。

怎么确认你当前使用的python版本支持哪些后缀的库能被import呢,很简单,执行下面的代码:

 

import importlib.machinery
print(importlib.machinery.all_suffixes())

Windows下输出的示例:

 

python .so文件 python import so 文件_python .so文件

Linux下输出的示例:

arm64 arch:

python .so文件 python import so 文件_import_02

x86_64 arch:

python .so文件 python import so 文件_so库_03

4.路径存在冲突,so分明在某个已包含的目录下存在,没有其他的错误,可还总是报错ModuleNotFoundError,这种情况也是很坑人的,花费了很久时间想不出原因来,就是没想到可能路径上存在重名的冲突,例如,我第一次使用python代码调用mediapipe时出现报错ModuleNotFoundError: No module named 'mediapipe.python._framework_bindings',其他什么错误原因都没发现,郁闷地熬夜,最后发现虽然/usr/local/lib/python3.6/site-packages/mediapipe/python/_framework_bindings.cpython-36m-x86_64-linux-gnu.so是存在的,但是在运行程序的工作目录下也有个使用过用来build出meaidpipe的wheel包的源码目录,由于当前工作目录加入了sys.path里最前面,于是python搜索路径时自然是优先找的/workspace/mediapipe/python/...,这个下面确实是没有那个so文件,于是把这里的mediapipe目录改名或者移走,问题就消失了。

5.最后,如果是自己实现的so库,要想能被python import,so库的内部实现按规范来。

  例如,使用g++编译代码的话要必须使用extern "C"限制编译生成的名字不会被改变,对要import的函数需要以对应的initxxx()来命名(使用g++编译时会把函数名字增加前后缀导致函数名被改变,编译出来的so被python import 时,会报类似下面的错误

ImportError: dynamic module does not define init function (initmyModule)

),https://csl.name/post/c-functions-python/这里有个很简单好懂的例子(注意里面是使用gcc编译的,所以没有加extern "C"约束,另外对于这种so库的编译,除了加-shared参数,最好加上-fPIC参数以去掉位置依赖,更多规则可参考python文档,例如https://docs.python.org/3.6/extending/extending.html):

    from myModule import *

#include <Python.h>

/*
 * Function to be called from Python
 */
static PyObject* py_myFunction(PyObject* self, PyObject* args)
{
  char *s = "Hello from C!";
  return Py_BuildValue("s", s);
}

/*
 * Another function to be called from Python
 */
static PyObject* py_myOtherFunction(PyObject* self, PyObject* args)
{
  double x, y;
  PyArg_ParseTuple(args, "dd", &x, &y);
  return Py_BuildValue("d", x*y);
}

static PyMethodDef myModule_methods[] = {
  {"myFunction", py_myFunction, METH_VARARGS},
  {"myOtherFunction", py_myOtherFunction, METH_VARARGS},
  {NULL, NULL}
};

extern "C" void void initmyModule()
{
  (void) Py_InitModule("myModule", myModule_methods);
}