1. 程式人生 > 其它 >python import找不到so庫的可能原因

python import找不到so庫的可能原因

技術標籤:Pythonpythonimport

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