Python3中__init__.py文件介绍
? ? ? Python中的模塊是包含Python定義和語句的文件(A module is a file containing Python definitions and statements),其文件名是模塊名加后綴名.py。在模塊內(nèi)部,通過全局變量__name__可以獲取模塊名。
? ? ? 模塊包含可執(zhí)行語句及函數(shù)定義。這些語句用于初始化模塊,且僅在import語句第一次遇到模塊名時執(zhí)行。
? ? ? 模塊有自己的私有符號表,用作模塊中所有函數(shù)的全局符號表。因此,在模塊內(nèi)使用全局變量時,不用擔(dān)心與用戶定義的全局變量發(fā)生沖突。另一方面,可以用與訪問模塊函數(shù)一樣的標(biāo)記法,訪問模塊的全局變量:modname.itemname
? ? ? 可以把其它模塊導(dǎo)入模塊。按慣例,所有import語句都放在模塊(或腳本)開頭,但這不是必須的。導(dǎo)入的模塊名存在導(dǎo)入模塊的全局符號表里。
? ? ? import語句有一個變體,可以直接把模塊里的名稱導(dǎo)入到另一個模塊的符號表,如下:這段代碼不會把模塊名foo導(dǎo)入到局部符號表里
from foo import foo_func, save
? ? ? 還有一種變體可以導(dǎo)入模塊內(nèi)定義的所有名稱,如下:這種方式會導(dǎo)入所有不以下劃線(_)開頭的名稱。大多數(shù)情況下,不要用這種功能,這種方式向解釋器導(dǎo)入了一批未知的名稱,可能會覆蓋已經(jīng)定義的名稱
from foo import *
? ? ? 注意,一般情況下,不建議從模塊或包導(dǎo)入*,因?yàn)?#xff0c;這項(xiàng)操作經(jīng)常讓代碼變得難以理解。不過,為了在交互式編譯器中少打幾個字,這么用也沒問題。
? ? ? 模塊名后使用as時,直接把a(bǔ)s后的名稱與導(dǎo)入模塊綁定,如下:與import foo一樣,這種方式也可以有效地導(dǎo)入模塊,唯一的區(qū)別是,導(dǎo)入的名稱是ex_foo
import foo as ex_foo
? ? ? from中也可以使用這種方式,效果類似,如下:
from foo import foo_func as foos
? ? ? 為了保證運(yùn)行效率,每次解釋器會話只導(dǎo)入一次模塊。如果更改了模塊內(nèi)容,必須重啟解釋器。
? ? ? 以腳本方式執(zhí)行模塊:這項(xiàng)操作將執(zhí)行模塊里的代碼,和導(dǎo)入模塊一樣,但會把__name__賦值為”__main__”: python foo.py <arguments>
? ? ? 模塊搜素路徑:導(dǎo)入spam模塊時,解釋器首先查找名為spam的內(nèi)置模塊。如果沒找到,解釋器再從sys.path變量中的目錄列表里查找spam.py文件。
? ? ? 為了快速加載模塊,Python把模塊的編譯版緩存在__pycache__目錄中,文件名為module.version.pyc,version對編譯文件格式進(jìn)行編碼,一般是Python的版本號。例如,CPython的3.8發(fā)行版中,foo.py的編譯版本(compiled version)緩存為__pycache__/foo.cpython-38.pyc。使用這種命名慣例,可以讓不同Python發(fā)行版及不同版本的已編譯模塊共存。Python對比編譯版本與源碼的修改日期,查看它是否已過期,是否要重新編譯,此過程完全自動化。此外,編譯模塊與平臺無關(guān),因此,可在不同架構(gòu)系統(tǒng)之間共享相同的支持庫。
? ? ? Python在兩種情況下不檢查緩存:(1).從命令行直接載入模塊,只重新編譯,不存儲編譯結(jié)果。(2).沒有源模塊,就不會檢查緩存。
? ? ? Python中的包(package)是一種通過使用”點(diǎn)式模塊名(dotted module names)”來構(gòu)建Python模塊命名空間的方法。例如,模塊名A.B表示包A中名為B的子模塊。導(dǎo)入包時,Python搜索sys.path里的目錄,查找包的子目錄。
? ? ? Python只把含__init__.py文件的目錄當(dāng)成包(Python3.2之后的版本不需要再額外的去專門創(chuàng)建一個__init__.py文件)。最簡情況下,__init__.py只是一個空文件,但該文件也可以執(zhí)行包的初始化代碼,或設(shè)置__all__變量。當(dāng)import指定的包時,此包內(nèi)的__init__.py會被隱性執(zhí)行,且只執(zhí)行一次。
? ? ? 注意:使用from package import item時,item可以是包的子模塊(或子包),也可以是包中定義的函數(shù)、類或變量等其它名稱。import語句首先測試包中是否定義了item;如果未在包中定義,則假定item是模塊,并嘗試加載。如果找不到item,則觸發(fā)ImportError異常。相反,使用import item.subitem.subsubitem句法時,除最后一項(xiàng)外,每個item都必須是包;最后一項(xiàng)可以是模塊或包,但不能是上一項(xiàng)中定義的類、函數(shù)或變量。
? ? ? import語句使用如下慣例:
? ? ? (1).如果包的__init__.py代碼定義了列表__all__,運(yùn)行from package import *時,它就是用于導(dǎo)入的模塊名列表。發(fā)布包的新版本時,包的作者應(yīng)更新此列表。即如果顯式聲明了__all__,import *就只會導(dǎo)入__all__列出的成員,如果__all__定義有誤,會拋出異常。
? ? ? (2).如果沒有定義__all__,from package.subpackage import *語句不會把包package.subpackage中所有子模塊都導(dǎo)入到當(dāng)前命名空間;該語句只確保導(dǎo)入包package.subpackage(可能還會運(yùn)行__init__.py中的初始化代碼),然后再導(dǎo)入包中定義的名稱。這些名稱包括__init__.py中定義的任何名稱(以及顯式加載的子模塊),還包括之前import語句顯式加載的包里的子模塊。
? ? ? 包中含有多個子包時,可以使用絕對導(dǎo)入引用兄弟包中的子模塊。還可以用import語句的from module import name形式執(zhí)行相對導(dǎo)入。這些導(dǎo)入語句使用前導(dǎo)句點(diǎn)表示相對導(dǎo)入中的當(dāng)前包(.)和父包(..),如下:
from . import echo
from .. import formats
? ? ? Python包有多種結(jié)構(gòu)。每個.py文件可作為單個模塊。__init__.py文件定義方式如下,也可以使用不同方式的組合:
? ? ? 第一種方式:from .module import *
? ? ? 優(yōu)點(diǎn):
? ? ? (1).用戶不需要知道模塊名稱,例如,哪個功能在哪個模塊中,他們只需要包名和函數(shù)名。
? ? ? (2).導(dǎo)入頂級包(import top-level package)后,用戶可以訪問任何功能。
? ? ? (3).制表符(Tab-completion)會補(bǔ)全為你提供的所有內(nèi)容。
? ? ? (4).當(dāng)模塊添加新特性時,不需要更新任何導(dǎo)入語句,它們將自動被包含。
? ? ? 缺點(diǎn):
? ? ? (1).要求所有函數(shù)和類必須命名唯一,即在模塊中不能含有相同名字的函數(shù)。在模塊中若含有相同名字的函數(shù),雖然可能不會報(bào)錯,但是調(diào)用的函數(shù)可能未必是你希望的那個。
? ? ? (2).如果包很大,它會向命名空間添加很多內(nèi)容,并且會減慢速度。
? ? ? (3).對于有些函數(shù)防止用戶調(diào)用的話,還需要作單獨(dú)處理,如使用下劃線來防止函數(shù)導(dǎo)入。
? ? ? 建議:
? ? ? (1).在難以預(yù)測用戶的工作流程時使用。
? ? ? (2).當(dāng)用戶可能經(jīng)常在不同模塊之間來回切換時使用。
? ? ? (3).當(dāng)用戶僅與幾個模塊(a few modules)一起使用。如果有很多模塊,新用戶可能更難在文檔中找到他們想要的功能。
? ? ? (4).在可能頻繁添加或刪除對象(object)時使用。
? ? ? 第二種方式:from .module import func 或 from .module import func1, func2
? ? ? 優(yōu)點(diǎn):
? ? ? (1).擁有第一種方式的所有優(yōu)點(diǎn),并更容易控制哪些對象可供用戶使用。
? ? ? 缺點(diǎn):
? ? ? (1).如果有許多模塊,并且每個模塊中有許多函數(shù),那么__init__.py中內(nèi)容會變得非常混亂。
? ? ? (2).當(dāng)向模塊中添加新類或函數(shù)時,它們也必須顯式添加到__init__.py文件中。
? ? ? 建議:
? ? ? (1).當(dāng)你的模塊由單個類組成時特別有用。
? ? ? (2).當(dāng)你有少量對象要導(dǎo)入時使用。
? ? ? (3).當(dāng)你的對象具有明確名稱時使用。
? ? ? (4).當(dāng)你確切地知道你的用戶需要哪些對象以及他們不需要哪些對象時使用。
? ? ? (5).當(dāng)你不希望頻繁添加大量需要導(dǎo)入的新模塊和對象時使用。
? ? ? 第三種方式:import package.module
? ? ? 優(yōu)點(diǎn):
? ? ? (1).簡化了__init__.py文件,僅在添加新的模塊時才需要更新。
? ? ? (2).它是靈活的,它可用于僅導(dǎo)入用戶需要的內(nèi)容或?qū)胨袃?nèi)容。
? ? ? (3).使用別名可以清理長的package.module規(guī)范(例如, import matplotlib.pyplot as plt)。
? ? ? (4).可以有多個同名對象(例如,foo和bar模塊中都調(diào)用了save()的函數(shù))。
? ? ? 缺點(diǎn):
? ? ? (1).一些導(dǎo)入方法會使代碼閱讀起來更復(fù)雜,例如,foo.foo_func()不指示foo來自哪個包。
? ? ? (2).最易讀的方法(import test_package,沒有別名)可能會導(dǎo)致長代碼塊(例如, test_package.foo.foo_func())使事情變得混亂。
? ? ? (3).用戶很難找到所有可能的功能。
? ? ? 建議:
? ? ? (1).當(dāng)你有一系列復(fù)雜的模塊時使用,其中大多數(shù)用戶永遠(yuǎn)不需要。
? ? ? (2).import test_package導(dǎo)入大量對象時使用,可能會很慢。
? ? ? (3).當(dāng)你可以為不同類型的用戶定義非常清晰的工作流程時使用。
? ? ? (4).當(dāng)你期望用戶能夠很好地瀏覽你的文檔時使用。
? ? ? 第四種方式:使用__all__
? ? ? 注:以上內(nèi)容及以下測試代碼主要來自于:
? ? ? 1.?https://towardsdatascience.com/whats-init-for-me-d70a312da583
? ? ? 2.?https://docs.python.org/zh-cn/3/tutorial/modules.html
? ? ? 測試代碼組織結(jié)構(gòu)如下:頂層目錄有一個test_package_main.py文件和一個test_package目錄,test_package目錄下有4個.py文件,各個文件內(nèi)容如下:
? ? ? test_package_main.py:
import sysvar = 4if var == 1: # 第一種方式,導(dǎo)入模塊中所有內(nèi)容import test_packagetest_package.foo_func()test_package.bar_func()test_package.baz_func()test_package.save() # 不能確定調(diào)用的是bar.py中的是還是foo.py中的. 由__init__.py決定,會調(diào)用后import的#test_package._calc() # AttributeError: module 'test_package' has no attribute '_calc'
elif var == 2: # 第二種方式,指定模塊中要導(dǎo)入的內(nèi)容,同一模塊中若導(dǎo)入多個函數(shù),中間用逗號分割: from .module import func1, func2import test_packagetest_package.foo_func()test_package.bar_func()test_package.baz_func()
elif var == 3: # 第三種方式# 用戶可以采用三種不同的使用方法method = 3if method == 1:import test_packagetest_package.foo.foo_func()test_package.bar.bar_func()test_package.baz.baz_func()elif method == 2:from test_package import foo, bar, baz # 從包中導(dǎo)入子模塊foo.foo_func()bar.bar_func()baz.baz_func()elif method == 3:import test_package.foo as ex_foo # 從包中導(dǎo)入單個模塊import test_package.bar as ex_barimport test_package.baz as ex_bazex_foo.foo_func()ex_bar.bar_func()ex_baz.baz_func()ex_foo.save()ex_bar.save()print("csdn addr:", ex_foo.csdn_addr)print("sys path:", sys.path)print("dir:", dir(ex_foo)) # dir為內(nèi)置函數(shù),獲取指定模塊的方法列表,包括變量、函數(shù)等等
elif var == 4: # 使用__all__from test_package import * # import * 就只會導(dǎo)入__all__列出的成員foo.foo_func()bar.bar_func()#baz.baz_func() # NameError: name 'baz' is not defined
? ? ? test_package/foo.py:
def foo_func():print("this is a foo function")def save():print("this is a foo save function")def _calc():print("this is a foo _calc function")github_addr = "https://github.com/fengbingchun"
csdn_addr = "https://blog.csdn.net/fengbingchun"
? ? ? test_package/bar.py:
def bar_func():print("this is a bar function")def save():print("this is a bar save function")def _calc():print("this is a bar _calc function")
? ? ? test_package/baz.py:
def baz_func():print("this is a baz function")
? ? ? test_package/__init__.py:
# reference: https://towardsdatascience.com/whats-init-for-me-d70a312da583var = 4print("import package: test_package")if var == 1: # 第一種方式,導(dǎo)入模塊中所有內(nèi)容# from .module import *from .foo import *from .bar import *from .baz import *
elif var == 2: # 第二種方式,指定模塊中要導(dǎo)入的內(nèi)容,同一模塊中若導(dǎo)入多個函數(shù),中間用逗號分割: from .module import func1, func2# from .module import funcfrom .foo import foo_funcfrom .bar import bar_funcfrom .baz import baz_func
elif var == 3: # 第三種方式import test_package.fooimport test_package.barimport test_package.baz
elif var == 4: # 使用__all____all__ = ["foo", "bar"]
? ? ? GitHub:https://github.com/fengbingchun/Python_Test
總結(jié)
以上是生活随笔為你收集整理的Python3中__init__.py文件介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python3中闭包介绍
- 下一篇: Python3中Pillow(PIL)介