一文教你如何解決Python開發(fā)總是import出錯的問題
摘要
有很多朋友碰到開發(fā)的過程中import包報錯的問題。今天想介紹一個可編輯安裝(Editable Install)模式, 方便快捷的解決這個問題。讓開發(fā)者的思路集中在業(yè)務(wù)的實現(xiàn)上。
1. 可編輯安裝(Editable Install)模式到底在解決什么問題?
在 Python 開發(fā)中,我們經(jīng)常需要修改本地包的代碼并實時測試效果。 而包這個概念可以說在Python開發(fā)中無處不在。 幾乎每天都要修改, 總不能每個包都重新反復(fù)的安裝吧。
可編輯安裝(Editable Install)模式正是為解決這一痛點而生,它允許開發(fā)者在無需重新安裝的情況下直接運行本地修改后的代碼。
2. 原理
可編輯安裝的本質(zhì)是通過符號鏈接(Symbolic Link)將包的源代碼目錄與 Python 環(huán)境中的包路徑關(guān)聯(lián)。當(dāng)執(zhí)行 pip install -e . 時:
- 創(chuàng)建鏈接:在系統(tǒng)目錄(如 site-packages)生成一個指向本地源碼目錄的符號鏈接。
- 動態(tài)加載:Python 導(dǎo)入包時,實際加載的是源碼目錄中的文件,修改后立即生效。
- 依賴隔離:包的依賴仍通過 requirements.txt 或 pyproject.toml 管理,確保環(huán)境一致性。
3. 最簡單的配置
3.1. 傳統(tǒng)項目(setup.py)
# 初始化項目結(jié)構(gòu) mkdir my_package && cd my_package touch setup.py # 包含包元數(shù)據(jù)(名稱、版本、依賴等) # 安裝可編輯模式 pip install -e .
3.2. 現(xiàn)代項目(pyproject.toml)
# pyproject.toml 示例 [build-system] requires = ["setuptools>=42", "wheel"] build-backend = "setuptools.build_meta" [project] name = "my_package" version = "0.1.0" dependencies = ["requests>=2.25"]
# 使用 PEP 660 標(biāo)準(zhǔn)安裝 pip install -e .
4. 實際項目配置
4.1. 本次項目結(jié)構(gòu)
本次項目,是針對各種AI功能的研究。 希望是通過統(tǒng)一項目結(jié)構(gòu)便于小朋友發(fā)揮想象開發(fā)有趣的功能。
環(huán)境管理使用Conda
PROJECT_ROOT
├─bin # 可執(zhí)行文件/Shell/Batch
│ ├─ run.bat # 啟動streamlit服務(wù)
│ ├─ install.py # `Editable Install` 安裝自動化腳本
│ └─ uninstall.py # `Editable Install` 卸載自動化腳本
├─config
├─docs # 項目文檔
├─input # 輸入文件
├─logs # 日志文件
├─output # 輸出文件
├─reports # 報告文件
├─resource # 資源文件
│ ├─image
│ └─excel
├─src # 項目代碼,遵循PEP 517/518標(biāo)準(zhǔn)
│ ├─tdouya_ai_assistant
│ ├─aliyun # 阿里云相關(guān)功能包
│ ├─basic
│ ├─common
│ │ └─log_record # 日志記錄功能
│ ├─game # 基于AI的游戲
│ ├─interface
│ │ └─pages
│ ├─ollama
│ ├─siliconflow
│ ├─tools # 工具封裝的包
│ └─volcengine
└─tests # 測試代碼
在這個項目中, 作為共通部分的common/log_record必然會被其他部分調(diào)用
4.2 創(chuàng)建必須的環(huán)境文件
4.2.1 pyproject.toml
在Python生態(tài)不斷演進(jìn)的今天,pyproject.toml已成為現(xiàn)代項目不可或缺的配置中樞。這個遵循TOML語法的文件不僅統(tǒng)一了構(gòu)建流程,更重塑了依賴管理和項目元數(shù)據(jù)的配置方式。本文將結(jié)合最新實踐,為您深度解析這個配置文件的架構(gòu)與應(yīng)用。
[build-system] requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "Tdouya-AI-Assistant" # ?? 用戶指定項目名稱 version = "0.1.1" authors = [{name = "田辛老師", email = "tianxin.xp@gmail.com"}] # ?? 用戶信息 description = "基于AI的智能助手開發(fā)框架" readme = "README.md" requires-python = ">=3.12" [tool.setuptools] package-dir = { "" = "src" } # 聲明包根目錄在src下
這里面比較重要的設(shè)定是最后一句:生命包的根目錄在src目錄下。 另外,這個文件中項目名必須為英文,中文親測不行。
4.2.2 setup.py
在Python生態(tài)中,setup.py曾是項目打包分發(fā)的標(biāo)準(zhǔn)配置文件,承載著將代碼轉(zhuǎn)化為可安裝包的使命。盡管現(xiàn)代工具鏈已轉(zhuǎn)向pyproject.toml,但理解這個經(jīng)典文件仍是掌握Python打包歷史的必修課。
from setuptools import setup, find_packages setup( name="Tdouya-AI-Assistant", # ?? 必須與pyproject.toml一致 version="0.1.1", author="田辛老師", author_email="tianxin.xp@gmail.com", description="基于AI的智能助手開發(fā)框架", packages=find_packages(where="src"), # 指定包搜索路徑 package_dir={"": "src"}, # 聲明包根目錄 install_requires=[ "python-dotenv>=0.19.0", "requests>=2.21.0", "streamlit>=1.0.0", "moviepy>=1.0.3", "colorlog>=6.7.0", "langchain_ollama", "langchain_core", "langchain_community", "ollama", "dashscope" ], python_requires=">=3.12", )
這樣做的目的是,
- 標(biāo)準(zhǔn)化項目元數(shù)據(jù)(名稱、版本、作者等)
- 聲明Python版本要求
- 定義包結(jié)構(gòu)和依賴關(guān)系
- 兼容現(xiàn)代構(gòu)建工具(poetry/flit)和傳統(tǒng)setuptools
4.2.3. 創(chuàng)建__init__.py文件
在各個模塊的目錄下, 創(chuàng)建__init__.py文件。
這個文件如何使用, 在我開始提到的文件中詳細(xì)介紹過。 在Python開發(fā)中非常靈活, 比如,我之前提到封裝的log_record包就把所有的代碼放到__init__.py中了
4.2.4. 執(zhí)行可編輯安裝
在命令行中執(zhí)行:
pip install -e .
這樣做可以實現(xiàn)目標(biāo):
- 將項目注冊為開發(fā)模式包
- 建立符號鏈接到Conda環(huán)境的site-packages
- 實現(xiàn)代碼修改即時生效
這樣做的結(jié)果是在src目錄下生成了一個符號鏈接的文件夾,如下圖。
內(nèi)容完全不用管, 只需要把這個文件放到Git忽略文件即可。
執(zhí)行完成后, 可以通過下面的方式測試一下:
PS E:\BG10-TRN-AIT-AI編程> python -c "import tdouya_ai_assistant; print(f'項目版本: {tdouya_ai_assistant}')"
項目版本: <module 'tdouya_ai_assistant' from 'E:\\BG10-TRN-AIT-AI編程\\src\\tdouya_ai_assistant\\__init__.py'>
5. 自動化
pip install -e . 這個寫法,加上卸載的命令, 實在是太容易忘了, 尤其是對新人來說, 于是貼心的給他們寫了一個安裝腳本,一個卸載腳本。主要的邏輯是, 找到項目根目錄, 然后將根目錄作為工作目錄執(zhí)行:
- 安裝 pip install -e .
- 卸載 pip uninstall -y Tdouya-AI-Assistant
兩個文件的源代碼田老師已經(jīng)給到大家了, 請參考:
5.1. 安裝腳本
# ============================================== # 文件名: init.py # 描述: 此腳本用于查找項目根路徑,若項目未初始化則進(jìn)行初始化操作,已初始化則給出提示。 # 作者: 田辛 # 創(chuàng)建日期: 2025-05-07 # ============================================== # 導(dǎo)入操作系統(tǒng)相關(guān)功能模塊,用于文件和目錄操作 import os # 導(dǎo)入子進(jìn)程模塊,用于創(chuàng)建新進(jìn)程執(zhí)行外部命令 import subprocess # 導(dǎo)入系統(tǒng)相關(guān)功能模塊,用于獲取Python解釋器信息 import sys # 導(dǎo)入日期時間模塊,用于獲取當(dāng)前時間 import datetime # 獲取當(dāng)前腳本所在的絕對路徑目錄 script_dir = os.path.dirname(os.path.abspath(__file__)) # 初始化當(dāng)前搜索目錄為腳本所在目錄 current_dir = script_dir while True: # 定義項目已初始化時所需的文件列表 required_files = [ "setup.py", "pyproject.toml", "README.md", "CONTRIBUTING.md", "installed.lock", ] # 檢查當(dāng)前目錄是否存在所有已初始化所需的文件 if all(os.path.isfile(os.path.join(current_dir, f)) for f in required_files): print( f"田豆芽AI助手已安裝, 若想再次安裝請先卸載。卸載命令:python {script_dir}/uninstall.py" ) break # 定義項目未初始化時所需的文件列表 original_required_files = [ "setup.py", "pyproject.toml", "README.md", "CONTRIBUTING.md", ] # 檢查當(dāng)前目錄是否存在所有未初始化所需的文件 if all( os.path.isfile(os.path.join(current_dir, f)) for f in original_required_files ): try: # 切換當(dāng)前工作目錄到項目根路徑 os.chdir(current_dir) # 使用當(dāng)前Python解釋器執(zhí)行 `pip install -e .` 命令進(jìn)行項目安裝 result = subprocess.run( [sys.executable, "-m", "pip", "install", "-e", "."], capture_output=True, text=True, check=True, ) print(result.stdout) # 生成鎖定文件路徑 lock_file_path = os.path.join(current_dir, "installed.lock") # 打開鎖定文件并寫入當(dāng)前時間戳 with open(lock_file_path, "w") as f: f.write(datetime.datetime.now().strftime("%Y%m%d %H:%M:%S")) print(f"田豆芽AI助手安裝完成。") except subprocess.CalledProcessError as e: # 捕獲子進(jìn)程執(zhí)行錯誤并輸出錯誤信息 print(f"執(zhí)行 `pip install -e .` 命令時出錯: {e.stderr}") except Exception as e: # 捕獲其他異常并輸出錯誤信息 print(f"執(zhí)行命令時出錯: {e}") break else: # 獲取當(dāng)前目錄的上一級目錄 new_dir = os.path.dirname(current_dir) # 檢查是否已經(jīng)回到根驅(qū)動器,如果是則表示未找到項目根路徑 if new_dir == current_dir[:3]: print("未找到項目根路徑。") break # 更新當(dāng)前搜索目錄為上一級目錄 current_dir = new_dir
5.2. 卸載腳本
# ============================================== # 文件名: uninstall.py # 描述: 此腳本用于查找項目根路徑,執(zhí)行卸載操作并刪除鎖定文件。 # 作者: 田辛 # 創(chuàng)建日期: 2025-05-07 # ============================================== import os import subprocess import sys # 獲取當(dāng)前腳本所在的絕對路徑目錄 script_dir = os.path.dirname(os.path.abspath(__file__)) # 初始化當(dāng)前搜索目錄為腳本所在目錄 current_dir = script_dir while True: # 定義項目已初始化時所需的文件列表 required_files = [ "setup.py", "pyproject.toml", "README.md", "CONTRIBUTING.md", "installed.lock", ] # 檢查當(dāng)前目錄是否存在所有已初始化所需的文件 if all(os.path.isfile(os.path.join(current_dir, f)) for f in required_files): try: # 切換當(dāng)前工作目錄到項目根路徑 os.chdir(current_dir) # 執(zhí)行pip uninstall命令 result = subprocess.run( [sys.executable, "-m", "pip", "uninstall", "-y", "Tdouya-AI-Assistant"], capture_output=True, text=True, check=True, ) print(result.stdout) # 刪除鎖定文件 lock_file_path = os.path.join(current_dir, "installed.lock") if os.path.isfile(lock_file_path): os.remove(lock_file_path) print("田豆芽AI助手卸載成功。") except subprocess.CalledProcessError as e: # 捕獲子進(jìn)程執(zhí)行錯誤并輸出錯誤信息 print(f"執(zhí)行 `pip uninstall Tdouya-AI-Assistant` 命令時出錯: {e.stderr}") except Exception as e: # 捕獲其他異常并輸出錯誤信息 print(f"執(zhí)行命令時出錯: {e}") break else: # 獲取當(dāng)前目錄的上一級目錄 new_dir = os.path.dirname(current_dir) # 檢查是否已經(jīng)回到根驅(qū)動器,如果是則表示未找到項目根路徑 if new_dir == current_dir[:3]: print("未找到項目根路徑。") break # 更新當(dāng)前搜索目錄為上一級目錄 current_dir = new_dir
6. 總結(jié)與展望:讓開發(fā)回歸創(chuàng)造本身
通過可編輯安裝模式,我們成功將Python開發(fā)體驗提升到新維度。這種模式帶來的不僅是「修改即生效」的技術(shù)便利,更是開發(fā)思維的解放——開發(fā)者無需再為包管理分心,可全身心投入業(yè)務(wù)邏輯的創(chuàng)新。
6.1 核心價值重述
效率革命:告別pip install的等待循環(huán),代碼修改秒級生效
環(huán)境穩(wěn)定:符號鏈接機(jī)制保障開發(fā)環(huán)境與生產(chǎn)環(huán)境的高度一致性
架構(gòu)自由:平級模塊間自由引用,支持微服務(wù)化開發(fā)范式
流程標(biāo)準(zhǔn)化:通過pyproject.toml+setup.py雙配置實現(xiàn)現(xiàn)代與傳統(tǒng)項目的兼容
6.2 最佳實踐建議
版本控制:將installed.lock文件納入Git管理,避免重復(fù)安裝
環(huán)境隔離:為每個項目創(chuàng)建獨立Conda環(huán)境,防止依賴污染
自動化升級:定期執(zhí)行pip list --outdated檢查依賴更新
跨平臺適配:Windows用戶需確認(rèn)符號鏈接權(quán)限(需開啟Developer Mode)
6.3 未來演進(jìn)方向
隨著Python打包工具鏈的持續(xù)演進(jìn),可編輯安裝模式正呈現(xiàn)三大趨勢:
- 與IDE深度集成:PyCharm/VSCode已原生支持開發(fā)模式包提示
- 容器化適配:在Docker開發(fā)環(huán)境中實現(xiàn)熱重載(需結(jié)合volume掛載)
- AI輔助調(diào)試:未來可能通過LLM自動分析循環(huán)依賴問題
到此這篇關(guān)于一文教你如何解決Python開發(fā)總是import出錯的問題的文章就介紹到這了,更多相關(guān)Python解決import出錯內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python打開文件、文件讀寫操作、with方式、文件常用函數(shù)實例分析
這篇文章主要介紹了Python打開文件、文件讀寫操作、with方式、文件常用函數(shù),結(jié)合實例形式分析了Python文件的打開、讀寫及常用文件操作函數(shù)使用技巧,需要的朋友可以參考下2020-01-01Django windows使用Apache實現(xiàn)部署流程解析
這篇文章主要介紹了Django windows使用Apache實現(xiàn)部署流程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10Python連接HDFS實現(xiàn)文件上傳下載及Pandas轉(zhuǎn)換文本文件到CSV操作
這篇文章主要介紹了Python連接HDFS實現(xiàn)文件上傳下載及Pandas轉(zhuǎn)換文本文件到CSV操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06Python 詳解通過Scrapy框架實現(xiàn)爬取CSDN全站熱榜標(biāo)題熱詞流程
Scrapy是用純Python實現(xiàn)一個為了爬取網(wǎng)站數(shù)據(jù)、提取結(jié)構(gòu)性數(shù)據(jù)而編寫的應(yīng)用框架,用途非常廣泛,框架的力量,用戶只需要定制開發(fā)幾個模塊就可以輕松的實現(xiàn)一個爬蟲,用來抓取網(wǎng)頁內(nèi)容以及各種圖片,非常之方便2021-11-11python實現(xiàn)網(wǎng)站微信登錄的示例代碼
這篇文章主要介紹了python實現(xiàn)網(wǎng)站微信登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09Python 中的 dataclass使用場景與代碼示例詳解
在Python中,dataclass是一個裝飾器,用于簡化類的定義,自動生成初始化、比較等方法,適用于需要存儲數(shù)據(jù)的場景,通過示例展示了dataclass的基本用法,感興趣的朋友跟隨小編一起看看吧2024-09-09python 動態(tài)獲取當(dāng)前運行的類名和函數(shù)名的方法
這篇文章主要介紹了python 動態(tài)獲取當(dāng)前運行的類名和函數(shù)名的方法,分別介紹使用內(nèi)置方法、sys模塊、修飾器、inspect模塊等方法,需要的朋友可以參考下2014-04-04Python中私有屬性“_“下劃線和“__“雙下劃線區(qū)別
本文主要介紹了Python中私有屬性“_“下劃線和“__“雙下劃線區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03