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