Python 簡明教程 --- 17,Python 模塊與包

正確的判斷來源于經(jīng)驗胎围,然而經(jīng)驗來源于錯誤的判斷吁系。
—— Fred Brooks

目錄

我們已經(jīng)知道函數(shù)是一種重復(fù)利用代碼的機制。

本節(jié)我們來介紹模塊白魂,Python 中的模塊汽纤,也是一種重復(fù)利用代碼的機制福荸。我們可以將有特定功能的代碼(比如函數(shù)蕴坪,等)寫在模塊中,供他人使用敬锐,便于重復(fù)利用背传,便于維護。

在前面的章節(jié)中台夺,我們也接觸過模塊径玖。Python 功能強大的一個重要的原因,就是它有各種方便使用的模塊谒养。

Python 中的模塊可以分為兩種:

  • 內(nèi)建模塊:Python 自帶的模塊挺狰,安裝好Python 解釋器后,內(nèi)建模塊就已經(jīng)安裝好了买窟,直接import 就可使用
  • 第三方模塊:由個人丰泊,公司或組織開發(fā)的模塊,使用之前需要先安裝始绍,再import

1瞳购,定義模塊

我們寫的Python 代碼都是以.py 為后綴,Python 模塊也是以.py 為后綴亏推,其實每個Python 代碼文件学赛,都可以看做是一個Python 模塊。

我們編寫如下代碼:

#! /usr/bin/env python3

# 定義一個字符串
PY = 'Hi Python.'

# 定義一個函數(shù)
def hello_py():
    print('Hello Python.') 

將該代碼寫在一個名為hello.py 的文件中吞杭,該文件就是一個hello 模塊盏浇,將該文件放在~/hi 目錄中。

注意:
在定義模塊時芽狗,要避免與已有的模塊起相同的名字绢掰,會引起沖突

2,使用模塊

我們進入~/hi 目錄童擎,打開python3 交互式環(huán)境滴劲。

dir() 函數(shù)

在引入模塊之前,我們先使用dir() 函數(shù)顾复,來查看當前環(huán)境可用的函數(shù)/變量

>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', '__name__', 
'__package__', '__spec__']

可以看到班挖,除了幾個魔法方法外,沒有任何其他可用的函數(shù)/變量芯砸。

我們定義一個變量s

>>> s = 'abc'

再用dir() 函數(shù)查看萧芙,可以看到多了一個s 變量:

>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', '__name__', 
'__package__', '__spec__', 's']

import 模塊

在使用模塊時,需要先引入模塊乙嘀。引入模塊末购,需要使用import 關(guān)鍵字。

我們輸入如下代碼將hello 模塊引入:

>>> import hello

再用dir() 函數(shù)虎谢,來查看當前環(huán)境可用的函數(shù)/變量

>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', '__name__', 
'__package__', '__spec__', 'hello', 's']

可以看到盟榴,多了一個hello。這是因為引入的模塊會使用模塊名來作為可用的標識符婴噩。

我們可以用dir(hello) 來查看hello 模塊支持的函數(shù)/變量

>>> dir(hello)
['__builtins__', '__cached__', '__doc__', 
'__file__', '__loader__', '__name__', 
'__package__', '__spec__', 'PY', 'hello_py']

可以看到擎场,有兩個標識符PYhello_py 可以使用,但并不能看出PYhello_py 是一個函數(shù)几莽,還是一個變量迅办。

可以通過type(hello.標識符) 來查看標識符的類型:

>>> type(hello.PY)               # 字符串
<class 'str'>
>>> type(hello.hello_py)         # 函數(shù)
<class 'function'>

可見PY 是一個字符串, hello_py 是一個函數(shù)章蚣。

注意:

引用一個模塊中的標識符站欺,使用的是點. 操作符

比如這里的hello.PYhello.hello_py

我們可以這樣來調(diào)用一個模塊中的函數(shù)/變量

>>> hello.PY
'Hi Python.'
>>> hello.hello_py()
hello python.

3,模塊文檔注釋

我們知道編寫函數(shù)時可以有文檔注釋,同樣模塊中也可以有文檔注釋矾策,任何模塊的第一個字符串將作為該模塊的文檔注釋磷账,如下:

#! /usr/bin/env python3

'''這是hello 模塊的說明文檔'''

# 定義一個字符串
PY = 'Hi Python.'

# 定義一個函數(shù)
def hello_py():
    print('Hello Python.') 

可以用魔法方法__doc__ 來訪問文檔字符串:

>>> hello.__doc__
'這是hello 模塊的說明文檔'

4,私有變量和私有函數(shù)

在Python 模塊中贾虽,規(guī)定以單下劃線_ 和雙下劃線__ 開頭的函數(shù)/變量逃糟,被稱為私有函數(shù)/變量(private),不應(yīng)該被外部模塊使用蓬豁。

不以___ 開頭的函數(shù)/變量绰咽,被稱為共有函數(shù)/變量 (public),可被外部使用地粪。

如下:

#! /usr/bin/env python3

'''這是hello 模塊的說明文檔'''

PY = 'Hi Python.'

def _hello(s):
    print('hello %s.' % s)

def hello_py():
    _hello('python')

def hello_java():
    _hello('java')

在該模塊中取募,hello_pyhello_javaPY 是提供給外部使用的蟆技,而_hello 只建議在模塊內(nèi)部使用矛辕。

其實在Python 中的這種訪問權(quán)限的控制,實際上是“假的”付魔,因為從技術(shù)上來說聊品,這種方式,并不能完全的禁止外部調(diào)用几苍,只是相當于一種編程規(guī)范翻屈。

如果我們非要使用這種以下劃線開頭的函數(shù),從技術(shù)上來說也是行得通的妻坝,如下:

>>> import hello
>>> hello._hello('python')
hello python.

但一般我們并不建議這樣做伸眶。

5,__name__ 屬性

每個模塊中都自帶一個__name__ 屬性刽宪,運行當前模塊時厘贼,該屬性的值為__main__,這經(jīng)常與if 語句一起圣拄,被用來測試代碼使用嘴秸,這類似其它編程語言中的main() 函數(shù)。

如下庇谆,文件名為hello.py

#! /usr/bin/env python3

'''這是hello 模塊的說明文檔'''

PY = 'Hi Python.'

def hello_py():
    print('hello python.')

if __name__ == '__main__':
    hello_py()

當我們在命令行執(zhí)行python3 hello.py 時岳掐,if __name__ == '__main__': 內(nèi)的代碼塊都會被執(zhí)行。

但當在其它模塊引入hello 模塊時饭耳,if __name__ == '__main__': 內(nèi)的代碼塊并不會被執(zhí)行串述。

6,Python 包

Python 包是比模塊更高層的概念寞肖,一個Python 包中可以包含一個或多個模塊纲酗。

一個包含多個模塊的Python 包結(jié)構(gòu)如下:

my_abc/
├── __init__.py
├── a.py
├── b.py
└── c/
    ├── __init__.py
    └── d.py

my_abc 是頂層包名衰腌,my_abc 目錄中有一個__init__.py 文件,擁有這個__init__.py 文件的目錄才會被認為是一個Python 包觅赊,否則桶唐,只是一個普通的目錄。

__init__.py 文件可以為空茉兰,也可以有Python 代碼,my_abc/c/ 目錄中的__init__.py 文件也是同樣的道理欣簇。

my_abc/__init__.py 文件中寫入如下內(nèi)容:

#! /usr/bin/env python3

print('這是my_abc 模塊中的 __init__ 文件')

my_abc/c/__init__.py 文件中寫入如下內(nèi)容:

#! /usr/bin/env python3

print('這是my_abc.c 模塊中的 __init__ 文件')

我們在my_abc 同級目錄下運行python3 解釋器规脸,當我們引入my_abc 包時,my_abc/__init__.py 文件中的代碼就會被執(zhí)行:

>>> import my_abc  # 第一次引入
這是my_abc 模塊中的 __init__ 文件
>>> import my_abc  # 第二次引入

可以看到當輸入import my_abc 代碼時熊咽,字符串這是my_abc 模塊中的 __init__ 文件 被輸出莫鸭,說明my_abc/__init__.py 文件中的內(nèi)容被執(zhí)行了。

緊接著再次輸入import my_abc 代碼時横殴,并沒有任何輸出被因,說明某個模塊中的__init__.py 文件中的內(nèi)容,只有在第一次導(dǎo)入該模塊時衫仑,才會執(zhí)行梨与。

此時,當前環(huán)境中多了一個可用的my_abc 標識符:

>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', '__name__', 
'__package__', '__spec__', 'my_abc']

同樣文狱,當我們引入my_abc.c 包時粥鞋,my_abc/c/__init__.py 文件中的代碼就會被執(zhí)行:

>>> import my_abc.c
這是my_abc.c 模塊中的 __init__ 文件

from ... import ... 語法

我們可以使用from... import... 語法來引入一個包中的模塊,比如我們引入my_abc 包中的a 模塊瞄崇,如下:

>>> from my_abc import a
這是my_abc 模塊中的 __init__ 文件
>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', '__name__', 
'__package__', '__spec__', 'a']

可以看到呻粹,使用該語法引入a 模塊時,my_abc/__init__.py 文件中的內(nèi)容也執(zhí)行了苏研,此時等浊,當前環(huán)境中多了一個可用的a 標識符。

from ... import ... as... 語法

可以使用as 關(guān)鍵字為模塊重命名摹蘑,例如將a 模塊重命名為a_test

>>> from my_abc import a as a_test
這是my_abc 模塊中的 __init__ 文件
>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', '__name__', 
'__package__', '__spec__', 'a_test']

可以看到本次導(dǎo)入時筹燕,沒有a 標識符,取而代之的a_test衅鹿。

from ... import * 語法

from ... import * 語法會導(dǎo)入某個包中__init__.py 文件內(nèi)的__all__ 列表的所有內(nèi)容庄萎,當__all__ 列表不存在時,該語法將導(dǎo)入__init__.py 文件中的所有可用標識符塘安。

例如糠涛,my_abc/__init__.py 文件中內(nèi)容如下,沒有__all__ 列表:

#! /usr/bin/env python3

print('這是my_abc 模塊中的 __init__ 文件')

i = 1
j = 2

def hello():
    print('hello')

使用from ... import * 語法兼犯,將導(dǎo)入__init__.py 文件中所有可用的標識符:

>>> from my_abc import *
這是my_abc 模塊中的 __init__ 文件
>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', '__name__', 
'__package__', '__spec__', 
'hello', 'i', 'j']

可見__init__.py 文件中的所有可用標識符忍捡,都被導(dǎo)入到了當前環(huán)境中集漾。

如果在__init__.py 文件中聲明一個__all__ 列表,如下:

#! /usr/bin/env python3

print('這是my_abc 模塊中的 __init__ 文件')

__all__ = ['i', 'hello']

i = 1
j = 2

def hello():
    print('hello') 

from ... import * 語法將只會導(dǎo)入__all__ 列表中的內(nèi)容砸脊,如下

>>> from my_abc import *
這是my_abc 模塊中的 __init__ 文件
>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', '__name__', 
'__package__', '__spec__', 
'hello', 'i']

因為__all__ 列表中只有i, hello 兩個元素具篇,所以只導(dǎo)入了這兩個標識符。

7凌埂,模塊的導(dǎo)入路徑

從內(nèi)建模塊sys 中的path 標識符可查看導(dǎo)入模塊的路徑:

>>> import sys
>>> sys.path
['', 
'/usr/lib/python36.zip', 
'/usr/lib/python3.6', 
'/usr/lib/python3.6/lib-dynload', 
'/usr/local/lib/python3.6/dist-packages', 
'/usr/lib/python3/dist-packages']

其中驱显,第一個'' 表示從當前目錄中導(dǎo)入,其它的目錄是系統(tǒng)中的標準包路徑瞳抓。

Python 會按照該列表埃疫,從前往后依次從這些路徑中導(dǎo)入你想import 的模塊,先從當前目錄中嘗試導(dǎo)入你想導(dǎo)入的模塊孩哑,如果沒有找到栓霜,會依次從后邊的路徑去查找,如果都沒有找到横蜒,最后會拋出異常胳蛮。

比如,現(xiàn)在有一個test 目錄丛晌,該目錄下有一個a.py 文件:

test/
└── a.py

如果在test 同級目錄下導(dǎo)入a.py仅炊,將出現(xiàn)異常,因為無法從sys.path 中找到a.py

>>> import a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'a'

此時澎蛛,可以將./test 目錄appendsys.path 中茂洒,然后再import

>>> import sys
>>> sys.path.append('./test')
>>> import a  # 沒有出現(xiàn)異常,說明導(dǎo)入成功
>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', '__name__', 
'__package__', '__spec__', 
'a', 'sys']    # a 模塊被成功導(dǎo)入

(完瓶竭。)


推薦閱讀:

Python 簡明教程 ---12督勺,Python 字典
Python 簡明教程 ---13,Python 集合
Python 簡明教程 ---14斤贰,Python 數(shù)據(jù)結(jié)構(gòu)進階
Python 簡明教程 ---15智哀,Python 函數(shù)
Python 簡明教程 ---16,Python 高階函數(shù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荧恍,一起剝皮案震驚了整個濱河市瓷叫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌送巡,老刑警劉巖摹菠,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骗爆,居然都是意外死亡次氨,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門摘投,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煮寡,“玉大人虹蓄,你說我怎么就攤上這事⌒宜海” “怎么了薇组?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坐儿。 經(jīng)常有香客問我律胀,道長,這世上最難降的妖魔是什么貌矿? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任炭菌,我火速辦了婚禮,結(jié)果婚禮上站叼,老公的妹妹穿的比我還像新娘。我一直安慰自己菇民,他們只是感情好尽楔,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著第练,像睡著了一般阔馋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娇掏,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天呕寝,我揣著相機與錄音,去河邊找鬼婴梧。 笑死下梢,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的塞蹭。 我是一名探鬼主播孽江,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼番电!你這毒婦竟也來了岗屏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤漱办,失蹤者是張志新(化名)和其女友劉穎这刷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娩井,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡暇屋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了洞辣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片率碾。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡叔营,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出所宰,到底是詐尸還是另有隱情绒尊,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布仔粥,位于F島的核電站婴谱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躯泰。R本人自食惡果不足惜谭羔,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望麦向。 院中可真熱鬧瘟裸,春花似錦、人聲如沸诵竭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卵慰。三九已至沙郭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間裳朋,已是汗流浹背病线。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鲤嫡,地道東北人送挑。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像暖眼,于是被迫代替她去往敵國和親让虐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容