
來(lái)源:麥?zhǔn)寰幊?
作者:麥?zhǔn)?
學(xué)習(xí)方法不對(duì),事倍功半!學(xué)習(xí)方法對(duì)了,事半功倍。
學(xué)編程,要先扎實(shí)的學(xué)好基礎(chǔ)語(yǔ)法和結(jié)構(gòu),剩下的就是不斷的實(shí)戰(zhàn)應(yīng)用,同時(shí)按需加強(qiáng)相關(guān)知識(shí)。
Python的包就是這里說(shuō)的基礎(chǔ)語(yǔ)法結(jié)構(gòu)之一。
把手放在胸口上,問(wèn)問(wèn)自己,你對(duì)Python包的了解有多少?然后認(rèn)真看完本文。你的今天一定是有進(jìn)步的。
包是基于模塊的,是對(duì)模塊的組織,建議和另一篇模塊文章一起看,融會(huì)貫通起來(lái)。模塊文章鏈接見(jiàn)文末往期推薦第1篇。
假設(shè)你已經(jīng)開(kāi)發(fā)了一個(gè)包含許多模塊的非常大的應(yīng)用程序。隨著模塊數(shù)量的增長(zhǎng),如果將它們都放到一個(gè)位置,則很難跟蹤所有模塊。如果它們有相似的名稱或功能,情況會(huì)更糟。你可能希望把他們放在不同的文件夾中,這就是Python中的包。
包(package)允許使用點(diǎn)表示法對(duì)模塊名稱空間進(jìn)行分層結(jié)構(gòu)。就像模塊可以避免全局變量名之間的沖突一樣,包也可以避免模塊名之間的沖突。
創(chuàng)建包非常簡(jiǎn)單,因?yàn)樗昧瞬僮飨到y(tǒng)固有的分層文件結(jié)構(gòu)。參考下面的目錄結(jié)構(gòu):
pkg ├── mod1.py └── mod2.py
這里有一個(gè)名為pkg的目錄,其中包含兩個(gè)模塊,mod1.py和mod2.py。模塊的內(nèi)容有:
mod1.py
def foo(): print('[mod1] foo()') class Foo: pass
mod2.py
def bar(): print('[mod2] bar()') class Bar: pass
根據(jù)這個(gè)結(jié)構(gòu),如果pkg目錄位于一個(gè)可以找到它的位置(在sys.path中包含的一個(gè)目錄中),你可以用點(diǎn)符號(hào)引用這兩個(gè)模塊(pkg.mod1, pkg.mod2),然后用你已經(jīng)熟悉的語(yǔ)法導(dǎo)入它們:
import <module_name>[, <module_name> ...]
>>> import pkg.mod1, pkg.mod2 >>> pkg.mod1.foo()
[mod1] foo() >>> x = pkg.mod2.Bar() >>> x
0x033F7290>
from import
>>> from pkg.mod1 import foo >>> foo()
[mod1] foo()
from import as
>>> from pkg.mod2 import Bar as Qux >>> x = Qux() >>> x
0x036DFFD0>
你也可以用這些語(yǔ)句來(lái)導(dǎo)入模塊:
from <package_name> import <modules_name>[, <module_name> ...]
from <package_name> import <module_name> as <alt_name>
>>> from pkg import mod1 >>> mod1.foo()
[mod1] foo() >>> from pkg import mod2 as quux >>> quux.bar()
[mod2] bar()
從技術(shù)上講,你也可以直接導(dǎo)入這個(gè)包:
>>> import pkg >>> pkg
<module 'pkg' (namespace)>
但這沒(méi)什么用。盡管嚴(yán)格地說(shuō),這是一個(gè)語(yǔ)法正確的Python語(yǔ)句,但它并沒(méi)有把pkg中的任何模塊放到本地命名空間中:
>>> pkg.mod1
Traceback (most recent call last):
File "" , line 1, in <module> pkg.mod1 AttributeError: module 'pkg'
has no attribute 'mod1' >>> pkg.mod1.foo()
Traceback (most recent call last):
File "" , line 1, in <module> pkg.mod1.foo() AttributeError: module '
pkg' has no attribute 'mod1' >>> pkg.mod2.Bar()
Traceback (most recent call last):
File "" , line 1, in <module> pkg.mod2.Bar() AttributeError: module '
pkg' has no attribute 'mod2'
要實(shí)際導(dǎo)入模塊或其內(nèi)容,需要使用上面展示的import例子。
如果一個(gè)名為__init__.py的文件存在于包目錄中,它會(huì)在導(dǎo)入包或包中的模塊時(shí)被調(diào)用。這可以用于執(zhí)行包初始化代碼,比如包級(jí)數(shù)據(jù)的初始化。
例如以下__init__.py文件:
__init__.py
print(f'Invoking __init__.py for {__name__}')
A = ['quux', 'corge', 'grault']
讓我們把上面例子中的這個(gè)文件添加到pkg目錄中:
pkg ├── __init__.py ├── mod1.py └── mod2.py
現(xiàn)在,當(dāng)包被導(dǎo)入時(shí),A就會(huì)被初始化:
>>> import pkg
Invoking __init__.py for pkg >>> pkg.A
['quux', 'corge', 'grault']
包中的模塊可以訪問(wèn)包里的全局變量:
mod1.py
def foo(): from pkg import A
print('[mod1] foo() / A = ', A) class Foo: pass
>>> from pkg import mod1
Invoking __init__.py for pkg >>> mod1.foo()
[mod1] foo() / A = ['quux', 'corge', 'grault']
__init__.py也可以用來(lái)實(shí)現(xiàn)從包中自動(dòng)導(dǎo)入模塊。例如,前面你看到import pkg語(yǔ)句只將名稱pkg放在調(diào)用者的局部符號(hào)表中,而不導(dǎo)入任何模塊。但是如果pkg目錄中的__init__.py包含以下內(nèi)容:
__init__.py
print(f'Invoking __init__.py for {__name__}') import pkg.mod1, pkg.mod2
然后當(dāng)你執(zhí)行import pkg,模塊mod1和mod2自動(dòng)導(dǎo)入:
>>> import pkg Invoking __init__.py for pkg >>> pkg.mod1.foo() [mod1] foo()
>>> pkg.mod2.bar() [mod2] bar()
注意:大部分Python文檔都聲明在創(chuàng)建包時(shí)必須在包目錄中存在__init__.py文件。這曾經(jīng)是必須的。過(guò)去,__init__.py的存在對(duì)Python來(lái)說(shuō)意味著正在定義一個(gè)包。該文件可以包含初始化代碼,甚至可以為空,但它必須存在。從Python 3.3開(kāi)始,引入了隱式命名空間包。這些允許創(chuàng)建一個(gè)沒(méi)有任何__init__.py文件的包。當(dāng)然,如果需要包初始化,它仍然可以存在。但現(xiàn)在不再是必須的了。
為了以下討論的目的,先前定義的包被擴(kuò)展以包含一些額外的模塊:
pkg ├── mod1.py ├── mod2.py ├── mod3.py └── mod4.py
pkg目錄中現(xiàn)在定義了四個(gè)模塊。其內(nèi)容如下:
mod1.py
def foo(): print('[mod1] foo()') class Foo: pass
mod2.py
def bar(): print('[mod2] bar()') class Bar: pass
mod3.py
def baz(): print('[mod3] baz()') class Baz: pass
mod4.py
def qux(): print('[mod4] qux()') class Qux: pass
正如你所看到,當(dāng)import *用于一個(gè)模塊時(shí),該模塊中的所有對(duì)象都被導(dǎo)入到本地符號(hào)表中,除了那些名稱以下劃線開(kāi)頭的對(duì)象:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__'] >>> from pkg.mod3 import *
>>> dir()
['Baz', '__annotations__', '__builtins__', '__doc__', '__loader__',
'__name__', '__package__', '__spec__', 'baz'] >>> baz()
[mod3] baz() >>> Baz
<class 'pkg.mod3.Baz'>
一個(gè)包的類似聲明是這樣的:
from import *
這行代碼做了什么呢?
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__'] >>> from pkg import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__',
'__name__', '__package__', '__spec__']
嗯。好像什么也沒(méi)做。你可能期望Python會(huì)深入到包目錄中,找到它所能找到的所有模塊,并將它們?nèi)繉?dǎo)入。但正如你所看到的,默認(rèn)情況下并不是這樣的。
相反,Python遵循以下約定:如果包目錄中的__init__.py文件包含名為__all__的列表,當(dāng)遇到import *語(yǔ)句時(shí),它將被視為應(yīng)該導(dǎo)入的模塊列表。
對(duì)于現(xiàn)在的例子,假設(shè)你像這樣在pkg目錄中創(chuàng)建一個(gè)__init__.py:
pkg/__init__.py
__all__ = [
'mod1',
'mod2',
'mod3',
'mod4' ]
現(xiàn)在用import *導(dǎo)入所有四個(gè)模塊:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__'] >>> from pkg import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'mod1', 'mod2', 'mod3', 'mod4'] >>> mod2.bar()
[mod2] bar() >>> mod4.Qux
<class 'pkg.mod4.Qux'>
使用import *仍然不被認(rèn)為是很好的形式,無(wú)論是對(duì)包還是模塊來(lái)說(shuō)都是如此。但是這個(gè)功能至少讓包的創(chuàng)建者對(duì)指定import *時(shí)發(fā)生的事情有一定的控制。(事實(shí)上,它提供了完全禁止它的能力,只要拒絕定義__all__就行了。如你所見(jiàn),包的默認(rèn)行為是不導(dǎo)入任何內(nèi)容。)
順便說(shuō)一下,__all__也可以在模塊中定義,并達(dá)到同樣的目的:控制import *導(dǎo)入的內(nèi)容。例如,修改mod1.py如下: pkg/mod1.py
__all__ = ['foo'] def foo(): print('[mod1] foo()') class Foo: pass
現(xiàn)在,pkg.mod1中的import *語(yǔ)句只會(huì)導(dǎo)入包含在__all__中的內(nèi)容:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__'] >>> from pkg.mod1 import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__',
'__name__', '__package__', '__spec__', 'foo'] >>> foo()
[mod1] foo() >>> Foo
Traceback (most recent call last):
File "" , line 1, in <module> Foo NameError: name 'Foo' is not defined
foo()(函數(shù))現(xiàn)在定義在本地命名空間中,但foo(類)沒(méi)有定義,因?yàn)楹笳卟辉?span style="color:#EF7060;">__all__中。
總之,當(dāng)import *被指定時(shí),__all__會(huì)被包和模塊用來(lái)控制導(dǎo)入的內(nèi)容。但是默認(rèn)行為是不同的:
對(duì)于一個(gè)包:當(dāng)__all__沒(méi)有定義,import *不導(dǎo)入任何東西。對(duì)于一個(gè)模塊:當(dāng)__all__沒(méi)有定義,import *導(dǎo)入所有內(nèi)容(除了以下劃線開(kāi)頭的名稱)。
包可以包含任意深度的嵌套子包。例如,讓我們對(duì)示例包目錄再做一個(gè)修改,如下所示:
pkg ├── sub_pkg1 │ ├── mod1.py │ └── mod2.py └── sub_pkg2
├── mod3.py └── mod4.py
四個(gè)模塊(mod1.py, mod2.py, mod3.py和mod4.py)的定義如前所述。但是現(xiàn)在,它們不是被集中到pkg目錄中,而是被分成兩個(gè)子目錄,sub_pkg1和sub_pkg2。
導(dǎo)入仍然和前面顯示的一樣工作。語(yǔ)法類似,但是額外的點(diǎn)符號(hào)用于分隔包名和子包名:
>>> import pkg.sub_pkg1.mod1 >>> pkg.sub_pkg1.mod1.foo()
[mod1] foo() >>> from pkg.sub_pkg1 import mod2 >>> mod2.bar()
[mod2] bar() >>> from pkg.sub_pkg2.mod3 import baz >>> baz()
[mod3] baz() >>> from pkg.sub_pkg2.mod4 import qux as grault >>> grault()
[mod4] qux()
此外,一個(gè)子包中的模塊可以引用同級(jí)子包中的對(duì)象(如果同級(jí)子包包含你需要的某些功能)。例如,假設(shè)你想從mod3模塊中導(dǎo)入并執(zhí)行mod1中的函數(shù)foo()。你可以使用絕對(duì)導(dǎo)入:
pkg/sub_pkg2/mod3.py
def baz(): print('[mod3] baz()') class Baz: pass
from pkg.sub_pkg1.mod1 import foo foo()
>>> from pkg.sub_pkg2 import mod3 [mod1] foo()
>>> mod3.foo() [mod1] foo()
或者你可以使用相對(duì)導(dǎo)入,其中..指的是上一級(jí)的包。從mod3.py中引用的話也就是sub_pkg2這一層。
..結(jié)果為父包(pkg),../sub_pkg1結(jié)果為子包sub_pkg1。
pkg/sub_pkg2/mod3.py
def baz(): print('[mod3] baz()') class Baz: pass from .. import sub_pkg1
print(sub_pkg1) from ..sub_pkg1.mod1 import foo
foo()
>>> from pkg.sub_pkg2 import mod3
<module 'pkg.sub_pkg1' (namespace)>
[mod1] foo()
數(shù)據(jù)分析咨詢請(qǐng)掃描二維碼
若不方便掃碼,搜微信號(hào):CDAshujufenxi
SQL Server 中 CONVERT 函數(shù)的日期轉(zhuǎn)換:從基礎(chǔ)用法到實(shí)戰(zhàn)優(yōu)化 在 SQL Server 的數(shù)據(jù)處理中,日期格式轉(zhuǎn)換是高頻需求 —— 無(wú)論 ...
2025-09-18MySQL 大表拆分與關(guān)聯(lián)查詢效率:打破 “拆分必慢” 的認(rèn)知誤區(qū) 在 MySQL 數(shù)據(jù)庫(kù)管理中,“大表” 始終是性能優(yōu)化繞不開(kāi)的話題。 ...
2025-09-18CDA 數(shù)據(jù)分析師:表結(jié)構(gòu)數(shù)據(jù) “獲取 - 加工 - 使用” 全流程的賦能者 表結(jié)構(gòu)數(shù)據(jù)(如數(shù)據(jù)庫(kù)表、Excel 表、CSV 文件)是企業(yè)數(shù)字 ...
2025-09-18DSGE 模型中的 Et:理性預(yù)期算子的內(nèi)涵、作用與應(yīng)用解析 動(dòng)態(tài)隨機(jī)一般均衡(Dynamic Stochastic General Equilibrium, DSGE)模 ...
2025-09-17Python 提取 TIF 中地名的完整指南 一、先明確:TIF 中的地名有哪兩種存在形式? 在開(kāi)始提取前,需先判斷 TIF 文件的類型 —— ...
2025-09-17CDA 數(shù)據(jù)分析師:解鎖表結(jié)構(gòu)數(shù)據(jù)特征價(jià)值的專業(yè)核心 表結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 規(guī)范存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如數(shù)據(jù)庫(kù)表、Excel 表、 ...
2025-09-17Excel 導(dǎo)入數(shù)據(jù)含缺失值?詳解 dropna 函數(shù)的功能與實(shí)戰(zhàn)應(yīng)用 在用 Python(如 pandas 庫(kù))處理 Excel 數(shù)據(jù)時(shí),“缺失值” 是高頻 ...
2025-09-16深入解析卡方檢驗(yàn)與 t 檢驗(yàn):差異、適用場(chǎng)景與實(shí)踐應(yīng)用 在數(shù)據(jù)分析與統(tǒng)計(jì)學(xué)領(lǐng)域,假設(shè)檢驗(yàn)是驗(yàn)證研究假設(shè)、判斷數(shù)據(jù)差異是否 “ ...
2025-09-16CDA 數(shù)據(jù)分析師:掌控表格結(jié)構(gòu)數(shù)據(jù)全功能周期的專業(yè)操盤(pán)手 表格結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如 Excel 表、數(shù)據(jù) ...
2025-09-16MySQL 執(zhí)行計(jì)劃中 rows 數(shù)量的準(zhǔn)確性解析:原理、影響因素與優(yōu)化 在 MySQL SQL 調(diào)優(yōu)中,EXPLAIN執(zhí)行計(jì)劃是核心工具,而其中的row ...
2025-09-15解析 Python 中 Response 對(duì)象的 text 與 content:區(qū)別、場(chǎng)景與實(shí)踐指南 在 Python 進(jìn)行 HTTP 網(wǎng)絡(luò)請(qǐng)求開(kāi)發(fā)時(shí)(如使用requests ...
2025-09-15CDA 數(shù)據(jù)分析師:激活表格結(jié)構(gòu)數(shù)據(jù)價(jià)值的核心操盤(pán)手 表格結(jié)構(gòu)數(shù)據(jù)(如 Excel 表格、數(shù)據(jù)庫(kù)表)是企業(yè)最基礎(chǔ)、最核心的數(shù)據(jù)形態(tài) ...
2025-09-15Python HTTP 請(qǐng)求工具對(duì)比:urllib.request 與 requests 的核心差異與選擇指南 在 Python 處理 HTTP 請(qǐng)求(如接口調(diào)用、數(shù)據(jù)爬取 ...
2025-09-12解決 pd.read_csv 讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)的科學(xué)計(jì)數(shù)法問(wèn)題 為幫助 Python 數(shù)據(jù)從業(yè)者解決pd.read_csv讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)時(shí)的科學(xué)計(jì)數(shù)法問(wèn)題 ...
2025-09-12CDA 數(shù)據(jù)分析師:業(yè)務(wù)數(shù)據(jù)分析步驟的落地者與價(jià)值優(yōu)化者 業(yè)務(wù)數(shù)據(jù)分析是企業(yè)解決日常運(yùn)營(yíng)問(wèn)題、提升執(zhí)行效率的核心手段,其價(jià)值 ...
2025-09-12用 SQL 驗(yàn)證業(yè)務(wù)邏輯:從規(guī)則拆解到數(shù)據(jù)把關(guān)的實(shí)戰(zhàn)指南 在業(yè)務(wù)系統(tǒng)落地過(guò)程中,“業(yè)務(wù)邏輯” 是連接 “需求設(shè)計(jì)” 與 “用戶體驗(yàn) ...
2025-09-11塔吉特百貨孕婦營(yíng)銷案例:數(shù)據(jù)驅(qū)動(dòng)下的精準(zhǔn)零售革命與啟示 在零售行業(yè) “流量紅利見(jiàn)頂” 的當(dāng)下,精準(zhǔn)營(yíng)銷成為企業(yè)突圍的核心方 ...
2025-09-11CDA 數(shù)據(jù)分析師與戰(zhàn)略 / 業(yè)務(wù)數(shù)據(jù)分析:概念辨析與協(xié)同價(jià)值 在數(shù)據(jù)驅(qū)動(dòng)決策的體系中,“戰(zhàn)略數(shù)據(jù)分析”“業(yè)務(wù)數(shù)據(jù)分析” 是企業(yè) ...
2025-09-11Excel 數(shù)據(jù)聚類分析:從操作實(shí)踐到業(yè)務(wù)價(jià)值挖掘 在數(shù)據(jù)分析場(chǎng)景中,聚類分析作為 “無(wú)監(jiān)督分組” 的核心工具,能從雜亂數(shù)據(jù)中挖 ...
2025-09-10統(tǒng)計(jì)模型的核心目的:從數(shù)據(jù)解讀到?jīng)Q策支撐的價(jià)值導(dǎo)向 統(tǒng)計(jì)模型作為數(shù)據(jù)分析的核心工具,并非簡(jiǎn)單的 “公式堆砌”,而是圍繞特定 ...
2025-09-10