python import找不到so庫的可能原因
在import一個so庫裡的類或函式時,有時發現so檔案分明就在那路徑下,可是總是報錯ModuleNotFoundError: No module named ***,這種錯誤的可能原因有:
1.首先要確保so所在的路徑已經包含在sys.path裡了,如果so所在目錄已經是在python預設的系統路徑裡,例如/usr/lib/python3.6/site-packages/或者/usr/local/lib/python3.6/site-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下輸出的示例:
Linux下輸出的示例:
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()來命名,https://csl.name/post/c-functions-python/這裡有個很簡單好懂的例子(注意裡面是使用gcc編譯的,所以沒有加extern "C"約束,另外對於這種so庫的編譯,除了加-shared引數,最好加上-fPIC引數以去掉位置依賴):
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);
}