一文帶你搞懂Python中__init__.py到底是什么
朋友們,今天我們來(lái)聊聊 Python 里一個(gè)低調(diào)卻至關(guān)重要的文件——__init__.py。
說(shuō)實(shí)話,這玩意兒剛開始學(xué) Python 時(shí),很多人(包括當(dāng)年的我)都是一臉懵:“這啥?刪了會(huì)咋樣?”
有些人可能聽說(shuō)過(guò)它是“包的標(biāo)志”,也有人覺(jué)得它“沒(méi)啥大用,可以忽略”,更有甚者以為它“只是個(gè)裝樣子的文件”。今天,我們就來(lái)徹底搞清楚 __init__.py 到底是干啥的,以及它如何影響 Python 項(xiàng)目的結(jié)構(gòu)和運(yùn)行。
先搞懂 Python 模塊(module)
在聊 __init__.py 之前,我們得先弄清楚 Python 里的 模塊 和 包 這兩個(gè)概念。
模塊(module):簡(jiǎn)單來(lái)說(shuō),就是一個(gè) .py 文件,里面寫了一些函數(shù)、類或者變量。
比如,有個(gè)叫 math_tools.py 的文件,里面有一堆數(shù)學(xué)工具函數(shù),那它就是個(gè)模塊。
# math_tools.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
然后,我們可以在別的 Python 文件里這樣用它:
import math_tools print(math_tools.add(3, 5)) # 輸出 8
這就是 模塊的基本用法,沒(méi)啥難的,對(duì)吧?
Python 包(package)是啥?
如果你寫的模塊越來(lái)越多,代碼量越來(lái)越大,就得想辦法組織它們。這時(shí)候,Python 里的 包(package) 就派上用場(chǎng)了。
包(package):一個(gè) 文件夾,里面包含多個(gè)模塊(.py 文件)。
在 Python 3.3 之前,如果要讓一個(gè)目錄被識(shí)別為 Python 包,必須在里面創(chuàng)建 __init__.py 文件。但從 Python 3.3 開始,即使沒(méi)有 __init__.py,Python 也能識(shí)別它是一個(gè)包(稱為“命名空間包”)。
不過(guò),大部分實(shí)際項(xiàng)目 依然建議添加 __init__.py,因?yàn)樗梢裕?/p>
? 明確這個(gè)文件夾是一個(gè)包,避免某些工具(如打包工具)識(shí)別錯(cuò)誤。
? 允許在包初始化時(shí)執(zhí)行特定代碼,比如自動(dòng)導(dǎo)入子模塊。
? 讓導(dǎo)入行為更加可控,避免意外的命名沖突。
比如,咱們有個(gè) math_utils 目錄,里面放了幾個(gè)數(shù)學(xué)相關(guān)的模塊:
math_utils/ # 這個(gè)文件夾就是一個(gè)包
│── __init__.py
│── basic.py
│── advanced.py
其中,basic.py 和 advanced.py 分別是兩個(gè)模塊,而 __init__.py 可以用來(lái) 自定義包的導(dǎo)入行為。
那么 __init__.py 到底是干嘛的?
雖然 __init__.py 不再是創(chuàng)建包的 必需 條件,但它依然是 Python 項(xiàng)目里一個(gè)重要的組件。
它的主要作用有 兩個(gè):
1.明確標(biāo)記目錄為 Python 包
如果 __init__.py 存在,Python 解析器就會(huì)知道:“這個(gè)目錄是個(gè) Python 包,而不是普通文件夾。”
即使 Python 3.3+ 之后不強(qiáng)制要求 __init__.py,但加上它可以:
? 避免 Python 解釋器在某些情況下誤認(rèn)為這是普通目錄。
? 兼容舊版本 Python,讓代碼能在不同環(huán)境中運(yùn)行得更穩(wěn)定。
? 讓某些工具(如 pytest、mypy)更好地識(shí)別項(xiàng)目結(jié)構(gòu)。
2.讓包能像模塊一樣被導(dǎo)入
如果 __init__.py 里什么都不寫,那它的作用只是個(gè)“標(biāo)志”。但如果我們?cè)?__init__.py 里加點(diǎn)代碼,它就能 自定義包的導(dǎo)入行為。
示例 1:讓包直接暴露子模塊
# math_utils/__init__.py from .basic import add, subtract from .advanced import power
這樣,我們就可以直接 import 整個(gè) math_utils,而不需要寫 .basic 或 .advanced 了:
import math_utils print(math_utils.add(2, 3)) # 輸出 5 print(math_utils.power(2, 3)) # 假設(shè) advanced 里有個(gè) power 函數(shù)
等于說(shuō),__init__.py 讓 包變得像一個(gè)大模塊 一樣,外部不需要知道里面的模塊結(jié)構(gòu),直接用就行。
示例 2:包初始化操作
__init__.py 還能在包被導(dǎo)入時(shí)執(zhí)行一些初始化操作,比如加載配置、設(shè)置日志等:
# math_utils/__init__.py
print("數(shù)學(xué)工具包加載成功!") # 只要 import 這個(gè)包,就會(huì)執(zhí)行這行代碼
__init__.py 還能干點(diǎn)啥
大廠的 Python 項(xiàng)目里,__init__.py 還經(jīng)常被用來(lái)做這些事:
1. 動(dòng)態(tài)導(dǎo)入子模塊
在大型 Python 項(xiàng)目中,隨著模塊越來(lái)越多,手動(dòng)維護(hù)__init__.py將變得特別復(fù)雜還容易出錯(cuò),這時(shí)候動(dòng)態(tài)導(dǎo)入子模塊就成了香餑餑了。
假設(shè)我們不知道 math_utils 里具體有哪些模塊,可以讓 __init__.py 在導(dǎo)入時(shí)動(dòng)態(tài)掃描并加載:
# math_utils/__init__.py
import os
import importlib
# 獲取當(dāng)前包的路徑
package_path = os.path.dirname(__file__)
# 遍歷當(dāng)前目錄下的所有 .py 文件(不包括 __init__.py 本身)
for module in os.listdir(package_path):
if module.endswith(".py") and module != "__init__.py":
module_name = module[:-3] # 去掉 .py 后綴
importlib.import_module(f"{__name__}.{module_name}") # 動(dòng)態(tài)導(dǎo)入模塊
效果: 這樣,當(dāng)你在別的地方寫 import mypackage,所有 mypackage 里的 .py 文件都會(huì)自動(dòng)加載,不用再手動(dòng) import 了!
沒(méi)加動(dòng)態(tài)導(dǎo)入要這么寫:
import math_utils.basic print(math_utils.basic.add(1,2)) #如果直接 import math_utils 會(huì)報(bào)錯(cuò)AttributeError: module 'math_utils' has no attribute 'basic'
加了動(dòng)態(tài)導(dǎo)入可以這么寫:
import math_utils print(math_utils.basic.add(1,2))
2. 控制對(duì)外暴露的模塊
有時(shí)候,我們不想讓 所有 子模塊都被自動(dòng)導(dǎo)入,而是只暴露一部分給外部用。這時(shí)候可以用 __all__ 來(lái) 手動(dòng)控制 允許被 from mypackage import * 訪問(wèn)的模塊。
# math_utils/__init__.py
import os
import importlib
package_path = os.path.dirname(__file__)
__all__ = []
for module in os.listdir(package_path):
if module.endswith(".py") and module != "__init__.py":
module_name = module[:-3]
__all__.append(module_name) # 只暴露在 __all__ 里的模塊
importlib.import_module(f"{__name__}.{module_name}")
效果:
from math_utils import * print(basic) # 只有在 __all__ 里的模塊能被導(dǎo)入
3. 懶加載(Lazy Import)
如果某些模塊比較大,加載它們會(huì)影響性能,那可以用 懶加載(lazy import)技術(shù),在需要時(shí)才導(dǎo)入,而不是在 import mypackage 時(shí)一次性全加載。
# math_utils/__init__.py
import importlib
def lazy_import(name):
return importlib.import_module(f"{__name__}.{name}")
module1 = lazy_import("basic")
效果:
這樣,basic 只有在第一次被使用時(shí)才會(huì)真正導(dǎo)入,提高了性能!
4. 做版本控制
__init__.py 還能給包加上版本號(hào),讓外部代碼可以訪問(wèn):
# math_utils/__init__.py __version__ = "1.0.0"
然后,在別的地方可以這樣用:
import math_utils print(math_utils.__version__) # 輸出 "1.0.0"
5. 隱藏內(nèi)部實(shí)現(xiàn)
有些模塊是“內(nèi)部用”的,不想讓外部訪問(wèn),怎么辦?可以在 __init__.py 里手動(dòng)控制 對(duì)外暴露的內(nèi)容:
# math_utils/__init__.py from .basic import add, subtract __all__ = ["add", "subtract"] # advanced.py 里的東西就不會(huì)被直接 import
這樣,外部只能用 math_utils.add(),但 math_utils.advanced 就不讓直接訪問(wèn)了。
結(jié)尾
關(guān)于 __init__.py,咱們就聊到這兒!希望這篇文章能幫你徹底搞懂它的作用,今后寫 Python 項(xiàng)目時(shí)能更自信地使用它。
到此這篇關(guān)于一文帶你搞懂Python中__init__.py到底是什么的文章就介紹到這了,更多相關(guān)Python __init__.py內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python抓取靜態(tài)網(wǎng)頁(yè)數(shù)據(jù)的完整指南
靜態(tài)網(wǎng)頁(yè)是指網(wǎng)頁(yè)的內(nèi)容在服務(wù)器上被創(chuàng)建和存儲(chǔ)時(shí)就已經(jīng)完全確定好了,本文將和大家詳細(xì)講講如何使用Python抓取靜態(tài)網(wǎng)頁(yè)數(shù)據(jù),感興趣的小伙伴可以了解下2025-11-11
從入門到實(shí)戰(zhàn)詳解Python實(shí)現(xiàn)辦公自動(dòng)化的最全指南
Python?辦公自動(dòng)化核心是用代碼替代重復(fù)的手動(dòng)操作,處理?Excel/Word/PPT、郵件、文件、網(wǎng)頁(yè)、PDF?等辦公場(chǎng)景,下面小編就和大家詳細(xì)介紹一下吧2026-06-06
Python通過(guò)matplotlib繪制動(dòng)畫簡(jiǎn)單實(shí)例
這篇文章主要介紹了Python通過(guò)matplotlib繪制動(dòng)畫簡(jiǎn)單實(shí)例,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12
一文詳解Python中三元運(yùn)算符的用法以及避坑指南
這篇文章主要為大家詳細(xì)介紹了Python中的三元運(yùn)算符(條件表達(dá)式),這是一種簡(jiǎn)潔的條件判斷語(yǔ)法結(jié)構(gòu),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2026-01-01
Python解決Flutter項(xiàng)目簡(jiǎn)體字問(wèn)題的方法
作為面向大陸外市場(chǎng)的應(yīng)用,我們經(jīng)常編寫代碼的時(shí)候往往忘記切換繁體字導(dǎo)致上線后出現(xiàn)簡(jiǎn)體字,因?yàn)檠芯肯聵I(yè)內(nèi)相關(guān)插件,看看怎么好解決這個(gè)問(wèn)題,OpenCC 支持語(yǔ)言比較多,所以基于此嘗試了用 Python 去實(shí)現(xiàn),需要的朋友可以參考下2024-07-07
Python進(jìn)行文件比對(duì)的不同場(chǎng)景實(shí)現(xiàn)方法詳解
Python提供了多個(gè)用于文件比對(duì)的庫(kù),適用于不同的比較場(chǎng)景,這篇文章將和大家詳細(xì)介紹一下這些文件比對(duì)庫(kù)的特點(diǎn)和具體使用,感興趣的小伙伴可以了解下2026-03-03
解決Django中調(diào)用keras的模型出現(xiàn)的問(wèn)題
今天小編就為大家分享一篇解決Django中調(diào)用keras的模型出現(xiàn)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-08-08

