Python基礎(chǔ)-26 __init__.py詳解

1.概述

? ? 在日常寫代碼的過(guò)程中,經(jīng)常能在很多地方看到__init__.py文件,似乎感覺(jué)沒(méi)有什么用,但有時(shí)候卻又不得不存在這個(gè)文件,而文件里面的內(nèi)容通常又是空,那這個(gè)文件到底有什么作用呢?今天讓我們就來(lái)探究一下。

2.Python的模塊和包

? ? 在Python代碼組織里面有兩個(gè)基本單位模塊(module)包(package),兩者之間的定義如下所示:

  • 模塊:簡(jiǎn)單來(lái)講就是一個(gè)py文件。里面定義了變量、函數(shù)、類、方法等,示例如下所示:
# @IDE:      PyCharm
# @Project:  PyCharmProjects
# @File:     hello.py
# @Time      2025-08-24 15:29
# @Author:   Surpass

def hello(name:str="surpass")->str:
    return f"Hello, {name}!"

def nationality(nationality:str="China")->str:
    if nationality == "China":
        return "I am Chinese"
    elif nationality == "Japan":
        return "I am Japanese"
    elif nationality == "USA":
        return "I am American"
    else:
        return "I am Astronaut"

? ? 使用方法如下所示:

# @IDE:      PyCharm
# @Project:  PyCharmProjects
# @File:     main.py
# @Time      2025-08-24 15:35
# @Author:   Surpass

import hello

if __name__ == '__main__':
    print(hello.hello())
    print(hello.nationality())

? ? 輸出結(jié)果如下所示:

Hello, surpass!
I am Chinese
  • 包:則是一個(gè)文件夾里面可以包含多個(gè)模塊文件,即在一個(gè)文件夾里面可以包含多個(gè)py文件。示例如下所示:
├─calcuator
│   ├─add.py
│   ├─sub.py
│   ├─__init__.py

? ? 使用方法如下所示:

from calcuator.add import add
from calcuator.sub import sub

if __name__ == '__main__':
    print(add(2, 3))
    print(sub(2, 3))

3.__init__.py文件在做什么

? ? 要理解__init__.py有什么作用,可以先看看這個(gè)文件在做什么操作?拿以上示例為例,文件目錄如下所示:

├─calcuator
│   ├─add.py
│   ├─sub.py
│   ├─__init__.py

? ? 各文件內(nèi)容如下所示:

# add.py
def add(a:int,b:int)->int:
    return a+b

# sub.py
def sub(a:int,b:int)->int:
    return a-b

# __init__.py
print("Call __init__")

# main.py
from calcuator.add import add
from calcuator.sub import sub

if __name__ == '__main__':
    print(add(2, 3))
    print(sub(2, 3))

? ? 在main.py運(yùn)行代碼,其結(jié)果如下所示:

Call __init__
5
-1

? ? 從輸出結(jié)果,可以看出__init__.py中的代碼也執(zhí)行了。則可以說(shuō)明,當(dāng)從一個(gè)包里面調(diào)用時(shí),__init__.py中的代碼會(huì)被首先執(zhí)行

4.__init__.py文件的作用

? ? 雖然__init__.py文件大部分情況文件內(nèi)容為空,但其功能非常靈活,常用用法如下所示。

4.1 標(biāo)識(shí)Python包

? ? 在模塊所在文件夾里面添加__init__.py后,則IDE會(huì)自動(dòng)將這個(gè)文件夾識(shí)別為Python包

4.2 簡(jiǎn)化模塊導(dǎo)入操作

? ? 假設(shè)包的目錄結(jié)構(gòu)如下所示:

├─calcuator
│   ├─add.py
│   ├─sub.py
│   ├─div.py
│   ├─mul.py
│   ├─__init__.py

? ? 如果要使用包里面的所有方法,常規(guī)寫法如下所示:

from calcuator.add import add
from calcuator.sub import sub
from calcuator.div import div
from calcuator.mul import mul

? ? 如果不想每次都寫這么麻煩,可以在__init__.py這樣寫

# __init__.py
from .add import add
from .sub import sub
from .mul import mul
from .div import div

print("Call __init__")

? ? 則在其他地方引用和調(diào)用時(shí),可以簡(jiǎn)寫為如下形式

import calcuator

if __name__ == '__main__':
    print(calcuator.add(2, 3))
    print(calcuator.sub(2, 3))

4.3 批量導(dǎo)入

? ? 如果一個(gè)模塊里面包含有很多變量、方法,而在調(diào)用時(shí),嫌麻煩不想一個(gè)個(gè)寫,則可以在__init__.py文件中這樣寫

# add.py
from typing import List

def add_number(a:int,b:int)->int:
    return a+b

def add_str(a:str,b:str)->str:
    return a+b

def add_list(a:List[int],b:List[int])->List[int]:
    return a+b

# __init__.py
from .add import *

? ? 在調(diào)用時(shí),可以這樣寫

# main.py
import calcuator

if __name__ == '__main__':
    print(calcuator.add_number(2, 3))
    print(calcuator.add_list([2], [3]))

通過(guò)這種方法,就可以實(shí)現(xiàn)批量導(dǎo)入,但需要注意導(dǎo)入存在有類和方法同名的情況,則遵從后面導(dǎo)入的會(huì)覆蓋先導(dǎo)入的

4.4 限定導(dǎo)入范圍

? ? 在模塊導(dǎo)入時(shí),需要遵從一個(gè)原則最小化導(dǎo)入原則,即需要使用哪些類、函數(shù)時(shí),僅導(dǎo)入這些類、函數(shù)即可,盡可能避免使用 import * 這種形式,因此__init__.py又可以改寫為這個(gè)樣子

from .add import add_number

? ? 這樣改寫后,在引用時(shí),僅會(huì)引用add_number一個(gè)方法,而這樣又會(huì)帶來(lái)另一個(gè)問(wèn)題,如果還要使用其他方法或模塊,該怎么辦呢?Python也提供了另一種方式,__init__.py可以改寫為這種形式

# 通過(guò) __all__ 顯式定義了要導(dǎo)入的模塊列表
__all__=[
    "add", # 對(duì)應(yīng)于add.py
    "div", # 對(duì)應(yīng)于div.py
    "sub"  # 對(duì)應(yīng)于sub.py
]

print("Call __init__")

? ? 在調(diào)用時(shí),雖然了可以使用 import * ,但在__init__.py已經(jīng)限定了引用的范圍。

from calcuator import *

if __name__ == '__main__':
    print(add.add_str(2, 3))
    print(mul.mul(2, 3))          # __init__.py已經(jīng)有限定,因此這里會(huì)出現(xiàn)報(bào)錯(cuò)

4.5 初始化操作

? ? 在前面的示例,我們已經(jīng)得知,在調(diào)用包時(shí)__init__.py會(huì)優(yōu)先執(zhí)行,因此也可以在里面進(jìn)行一些初始化操作。示例如下所示:

# __init__.py

import platform
import os

# 通過(guò)__all__ 顯式定義了要導(dǎo)入的模塊列表
__all__=[
    "add", # 對(duì)應(yīng)于add.py
    "div", # 對(duì)應(yīng)于div.py
    "sub",  # 對(duì)應(yīng)于sub.py
]

# 演示在 __init__.py 里進(jìn)行初始化操作

if platform.system() == "Windows":
    os.makedirs(r"F:\python_test",exist_ok=True)
elif platform.system() in ["Darwin","Linux"]:
    os.makedirs(r"/tmp",exist_ok=True)
else:
    exit(255)

print("Call __init__")

4.6 版本管理

? ? 在存在多人協(xié)作時(shí),方便快速確認(rèn)版本,示例代碼如下所示:

# __init__.py
__version__ = "8.9.0"

print("Call __init__")

# main.py
import calcuator

if __name__ == '__main__':
    print(calcuator.__version__)

4.7 動(dòng)態(tài)加載

? ? 在一些大型項(xiàng)目中,如果包里面的模塊太多,一個(gè)個(gè)寫,很容易出現(xiàn)遺漏的情況,于是就出現(xiàn)動(dòng)態(tài)導(dǎo)入的情況,示例如下所示:

# __init__.py
import os
import importlib

__version__ = "8.9.0"

current_path=os.path.dirname(__file__)

for module in os.listdir(current_path):
    if module.endswith(".py") and module != "__init__.py":
        module_name=module[:-3]
        import_module=f"{__name__}.{module_name}"
        print(f"import {import_module}")
        importlib.import_module(f"{__name__}.{module_name}")


print("Call __init__")

# main.py
import calcuator

if __name__ == '__main__':
    print(calcuator.__version__)
    print(calcuator.add.add_number(1,2))
    print(calcuator.mul.mul(10, 20))

? ? 運(yùn)行結(jié)果如下所示:

import calcuator.add
import calcuator.div
import calcuator.mul
import calcuator.sub
Call __init__
8.9.0
3
200

5. __init__.py最佳實(shí)踐

? ? 雖然__init__.py使用非常靈活,但也遵循以下建議:

  • 在項(xiàng)目中,一定添加這個(gè)文件,用以標(biāo)識(shí)為python包
  • __init__.py同樣可以添加代碼,但盡量不要使用過(guò)多的邏輯,否則在導(dǎo)入包的時(shí)候會(huì)影響性能
  • 合理使用__all__從而避免暴露過(guò)多的內(nèi)部細(xì)節(jié)
  • 動(dòng)態(tài)加載功能很好,但也可能會(huì)帶來(lái)性能問(wèn)題
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容