微信公衆號:碼農充電站pro
我的主頁:https://codeshellme.github.iohtml
正確的判斷來源於經驗,然而經驗來源於錯誤的判斷。
—— Fred Brooksjava
目錄python
咱們已經知道函數
是一種重複利用代碼的機制。git
本節咱們來介紹模塊
,Python 中的模塊,也是一種重複利用代碼的機制。咱們能夠將有特定功能
的代碼(好比函數
,類
等)寫在模塊中,供他人使用,便於重複利用,便於維護。github
在前面的章節中,咱們也接觸過模塊。Python 功能強大的一個重要的緣由,就是它有各類方便使用的模塊。shell
Python 中的模塊能夠分爲兩種:編程
import
就可以使用import
咱們寫的Python 代碼都是以.py
爲後綴,Python 模塊也是以.py
爲後綴,其實每一個Python 代碼文件,均可以看作是一個Python 模塊。微信
咱們編寫以下代碼:數據結構
#! /usr/bin/env python3 # 定義一個字符串 PY = 'Hi Python.' # 定義一個函數 def hello_py(): print('Hello Python.')
將該代碼寫在一個名爲hello.py
的文件中,該文件就是一個hello
模塊,將該文件放在~/hi
目錄中。app
注意:
在定義模塊時,要避免與已有的模塊起相同的名字,會引發衝突
咱們進入~/hi
目錄,打開python3
交互式環境。
dir()
函數
在引入模塊以前,咱們先使用dir()
函數,來查看當前環境可用的函數/變量
:
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
能夠看到,除了幾個魔法方法
外,沒有任何其餘可用的函數/變量
。
咱們定義一個變量s
:
>>> s = 'abc'
再用dir()
函數查看,能夠看到多了一個s
變量:
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 's']
import
模塊
在使用模塊時,須要先引入模塊。引入模塊,須要使用import
關鍵字。
咱們輸入以下代碼將hello
模塊引入:
>>> import hello
再用dir()
函數,來查看當前環境可用的函數/變量
:
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'hello', 's']
能夠看到,多了一個hello
。這是由於引入的模塊會使用模塊名
來做爲可用的標識符。
咱們能夠用dir(hello)
來查看hello
模塊支持的函數/變量
:
>>> dir(hello) ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'PY', 'hello_py']
能夠看到,有兩個標識符PY
和 hello_py
可使用,但並不能看出PY
和hello_py
是一個函數,仍是一個變量。
能夠經過type(hello.標識符)
來查看標識符的類型:
>>> type(hello.PY) # 字符串 <class 'str'> >>> type(hello.hello_py) # 函數 <class 'function'>
可見PY
是一個字符串, hello_py
是一個函數。
注意:
引用一個模塊中的
標識符
,使用的是點.
操做符好比這裏的
hello.PY
和hello.hello_py
咱們能夠這樣來調用一個模塊中的函數/變量
:
>>> hello.PY 'Hi Python.' >>> hello.hello_py() hello python.
咱們知道編寫函數時能夠有文檔註釋,一樣模塊中也能夠有文檔註釋
,任何模塊的第一個字符串將做爲該模塊的文檔註釋,以下:
#! /usr/bin/env python3 '''這是hello 模塊的說明文檔''' # 定義一個字符串 PY = 'Hi Python.' # 定義一個函數 def hello_py(): print('Hello Python.')
能夠用魔法方法__doc__
來訪問文檔字符串:
>>> hello.__doc__ '這是hello 模塊的說明文檔'
在Python 模塊中,規定以單下劃線_
和雙下劃線__
開頭的函數/變量
,被稱爲私有函數/變量
(private),不該該被外部模塊使用。
不以_
和__
開頭的函數/變量
,被稱爲共有函數/變量
(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
只建議在模塊內部使用。
其實在Python 中的這種訪問權限
的控制,其實是「假的」,由於從技術上來講,這種方式,並不能徹底的禁止外部調用,只是至關於一種編程規範
。
若是咱們非要使用這種以下劃線
開頭的函數,從技術上來講也是行得通的,以下:
>>> import hello >>> hello._hello('python') hello python.
但通常咱們並不建議這樣作。
__name__
屬性每一個模塊中都自帶一個__name__
屬性,運行當前模塊時,該屬性的值爲__main__
,這常常與if 語句
一塊兒,被用來測試代碼
使用,這相似其它編程語言中的main()
函數。
以下,文件名爲hello.py
:
#! /usr/bin/env python3 '''這是hello 模塊的說明文檔''' PY = 'Hi Python.' def hello_py(): print('hello python.') if __name__ == '__main__': hello_py()
當咱們在命令行執行python3 hello.py
時,if __name__ == '__main__':
內的代碼塊都會被執行。
但當在其它模塊引入hello
模塊時,if __name__ == '__main__':
內的代碼塊並不會被執行。
Python 包是比模塊更高層的概念,一個Python 包中能夠包含一個或多個模塊。
一個包含多個模塊的Python 包結構以下:
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
文件中寫入以下內容:
#! /usr/bin/env python3 print('這是my_abc 模塊中的 __init__ 文件')
向my_abc/c/__init__.py
文件中寫入以下內容:
#! /usr/bin/env python3 print('這是my_abc.c 模塊中的 __init__ 文件')
咱們在my_abc
同級目錄下運行python3
解釋器,當咱們引入my_abc
包時,my_abc/__init__.py
文件中的代碼就會被執行:
>>> import my_abc # 第一次引入 這是my_abc 模塊中的 __init__ 文件 >>> import my_abc # 第二次引入
能夠看到當輸入import my_abc
代碼時,字符串這是my_abc 模塊中的 __init__ 文件
被輸出,說明my_abc/__init__.py
文件中的內容被執行了。
緊接着再次輸入import my_abc
代碼時,並無任何輸出,說明某個模塊中的__init__.py
文件中的內容,只有在第一次導入該模塊時,纔會執行。
此時,當前環境中多了一個可用的my_abc
標識符:
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'my_abc']
一樣,當咱們引入my_abc.c
包時,my_abc/c/__init__.py
文件中的代碼就會被執行:
>>> 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
文件中的內容也執行了,此時,當前環境中多了一個可用的a
標識符。
from ... import ... as...
語法
可使用as
關鍵字爲模塊重命名,例如將a
模塊重命名爲a_test
:
>>> from my_abc import a as a_test 這是my_abc 模塊中的 __init__ 文件 >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a_test']
能夠看到本次導入時,沒有a
標識符,取而代之的a_test
。
from ... import *
語法
from ... import *
語法會導入某個包中__init__.py
文件內的__all__
列表的全部內容,當__all__
列表不存在時,該語法將導入__init__.py
文件中的全部可用標識符。
例如,my_abc/__init__.py
文件中內容以下,沒有__all__
列表:
#! /usr/bin/env python3 print('這是my_abc 模塊中的 __init__ 文件') i = 1 j = 2 def hello(): print('hello')
使用from ... import *
語法,將導入__init__.py
文件中全部可用的標識符:
>>> from my_abc import * 這是my_abc 模塊中的 __init__ 文件 >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'hello', 'i', 'j']
可見__init__.py
文件中的全部可用標識符,都被導入到了當前環境中。
若是在__init__.py
文件中聲明一個__all__
列表,以下:
#! /usr/bin/env python3 print('這是my_abc 模塊中的 __init__ 文件') __all__ = ['i', 'hello'] i = 1 j = 2 def hello(): print('hello')
from ... import *
語法將只會導入__all__
列表中的內容,以下
>>> from my_abc import * 這是my_abc 模塊中的 __init__ 文件 >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'hello', 'i']
由於__all__
列表中只有i
, hello
兩個元素,因此只導入了這兩個標識符。
從內建模塊sys
中的path
標識符可查看導入模塊的路徑:
>>> 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']
其中,第一個''
表示從當前目錄
中導入,其它的目錄是系統中的標準包路徑
。
Python 會按照該列表,從前日後依次從這些路徑中導入你想import
的模塊,先從當前目錄中嘗試導入你想導入的模塊,若是沒有找到,會依次從後邊的路徑去查找,若是都沒有找到,最後會拋出異常。
好比,如今有一個test
目錄,該目錄下有一個a.py
文件:
test/ └── a.py
若是在test
同級目錄下導入a.py
,將出現異常,由於沒法從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 # 沒有出現異常,說明導入成功 >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'sys'] # a 模塊被成功導入
(完。)
推薦閱讀:
Python 簡明教程 --- 14,Python 數據結構進階
Python 簡明教程 --- 16,Python 高階函數
歡迎關注做者公衆號,獲取更多技術乾貨。