一文帶你搞懂Python中__init__.py到底是什么
朋友們,今天我們來聊聊 Python 里一個(gè)低調(diào)卻至關(guān)重要的文件——__init__.py
。
說實(shí)話,這玩意兒剛開始學(xué) Python 時(shí),很多人(包括當(dāng)年的我)都是一臉懵:“這啥?刪了會(huì)咋樣?”
有些人可能聽說過它是“包的標(biāo)志”,也有人覺得它“沒啥大用,可以忽略”,更有甚者以為它“只是個(gè)裝樣子的文件”。今天,我們就來徹底搞清楚 __init__.py
到底是干啥的,以及它如何影響 Python 項(xiàng)目的結(jié)構(gòu)和運(yùn)行。
先搞懂 Python 模塊(module)
在聊 __init__.py
之前,我們得先弄清楚 Python 里的 模塊 和 包 這兩個(gè)概念。
模塊(module):簡(jiǎn)單來說,就是一個(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
這就是 模塊的基本用法,沒啥難的,對(duì)吧?
Python 包(package)是啥?
如果你寫的模塊越來越多,代碼量越來越大,就得想辦法組織它們。這時(shí)候,Python 里的 包(package) 就派上用場(chǎng)了。
包(package):一個(gè) 文件夾,里面包含多個(gè)模塊(.py
文件)。
在 Python 3.3 之前,如果要讓一個(gè)目錄被識(shí)別為 Python 包,必須在里面創(chuàng)建 __init__.py
文件。但從 Python 3.3 開始,即使沒有 __init__.py
,Python 也能識(shí)別它是一個(gè)包(稱為“命名空間包”)。
不過,大部分實(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
可以用來 自定義包的導(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ù)
等于說,__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)常被用來做這些事:
1. 動(dòng)態(tài)導(dǎo)入子模塊
在大型 Python 項(xiàng)目中,隨著模塊越來越多,手動(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
了!
沒加動(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__
來 手動(dòng)控制 允許被 from mypackage import *
訪問的模塊。
# 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),讓外部代碼可以訪問:
# 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)部用”的,不想讓外部訪問,怎么辦?可以在 __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
就不讓直接訪問了。
結(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實(shí)現(xiàn)批量修改指定目錄下圖片的大小
批量修改指定目錄下圖片大小通常是在需要對(duì)大量圖片進(jìn)行統(tǒng)一處理的情況下使用的,本文主要為大家詳細(xì)介紹了如何利用Python實(shí)現(xiàn)批量修改圖片大小,需要的可以參考下2023-10-10Python實(shí)現(xiàn)FTP文件傳輸?shù)膶?shí)例
在本篇文章里小編給各位分享的是關(guān)于Python實(shí)現(xiàn)FTP文件傳輸?shù)膶?shí)例以及相關(guān)代碼,需要的朋友們學(xué)習(xí)下。2019-07-07python異步的ASGI與Fast Api實(shí)現(xiàn)
本文主要介紹了python異步的ASGI與Fast Api實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07Python遍歷目錄下文件、讀取、千萬條數(shù)據(jù)合并詳情
這篇文章主要介紹了Python遍歷目錄下文件、讀取、千萬條數(shù)據(jù)合并詳情,對(duì)文件夾和文件進(jìn)行屬性判斷,首先對(duì)文件夾進(jìn)行遍歷,看文件夾里有什么樣的文件,讀取出文件夾中的所有文件,下面文章將詳細(xì)介紹該內(nèi)容,需要的小伙伴可以參考一下2022-01-01GPU狀態(tài)監(jiān)測(cè)?nvidia-smi?命令的用法詳解
這篇文章主要介紹了GPU狀態(tài)監(jiān)測(cè)?nvidia-smi?命令的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11解決python繪圖使用subplots出現(xiàn)標(biāo)題重疊的問題
這篇文章主要介紹了python繪圖使用subplots出現(xiàn)標(biāo)題重疊的問題及解決方法,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04python計(jì)算圓周長(zhǎng)、面積、球體體積并畫出圓
這篇文章主要介紹了python計(jì)算圓周長(zhǎng)、面積、球體體積并畫出圓(python3+PyObject+Gtk實(shí)現(xiàn)界面聯(lián)動(dòng)),需要的朋友可以參考下2014-04-04