正確的判斷來源于經(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']
可以看到擎场,有兩個標識符PY
和 hello_py
可以使用,但并不能看出PY
和hello_py
是一個函數(shù)几莽,還是一個變量迅办。
可以通過type(hello.標識符)
來查看標識符的類型:
>>> type(hello.PY) # 字符串
<class 'str'>
>>> type(hello.hello_py) # 函數(shù)
<class 'function'>
可見PY
是一個字符串, hello_py
是一個函數(shù)章蚣。
注意:
引用一個模塊中的
標識符
站欺,使用的是點.
操作符比如這里的
hello.PY
和hello.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_py
,hello_java
和 PY
是提供給外部使用的蟆技,而_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
目錄append
到sys.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ù)