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

微信公衆號:碼農充電站pro
我的主頁:https://codeshellme.github.iohtml

正確的判斷來源於經驗,然而經驗來源於錯誤的判斷。
—— Fred Brooksjava

目錄python

在這裏插入圖片描述

咱們已經知道函數是一種重複利用代碼的機制。git

本節咱們來介紹模塊,Python 中的模塊,也是一種重複利用代碼的機制。咱們能夠將有特定功能的代碼(好比函數等)寫在模塊中,供他人使用,便於重複利用,便於維護。github

在前面的章節中,咱們也接觸過模塊。Python 功能強大的一個重要的緣由,就是它有各類方便使用的模塊。shell

Python 中的模塊能夠分爲兩種:編程

  • 內建模塊:Python 自帶的模塊,安裝好Python 解釋器後,內建模塊就已經安裝好了,直接import 就可以使用
  • 第三方模塊:由我的,公司或組織開發的模塊,使用以前須要先安裝,再import

1,定義模塊

咱們寫的Python 代碼都是以.py 爲後綴,Python 模塊也是以.py 爲後綴,其實每一個Python 代碼文件,均可以看作是一個Python 模塊。微信

咱們編寫以下代碼:數據結構

#! /usr/bin/env python3

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

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

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

注意:
在定義模塊時,要避免與已有的模塊起相同的名字,會引發衝突

2,使用模塊

咱們進入~/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']

能夠看到,有兩個標識符PYhello_py 可使用,但並不能看出PYhello_py 是一個函數,仍是一個變量。

能夠經過type(hello.標識符) 來查看標識符的類型:

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

可見PY 是一個字符串, hello_py 是一個函數。

注意:

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

好比這裏的hello.PYhello.hello_py

咱們能夠這樣來調用一個模塊中的函數/變量

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

3,模塊文檔註釋

咱們知道編寫函數時能夠有文檔註釋,一樣模塊中也能夠有文檔註釋,任何模塊的第一個字符串將做爲該模塊的文檔註釋,以下:

#! /usr/bin/env python3

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

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

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

能夠用魔法方法__doc__ 來訪問文檔字符串:

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

4,私有變量和私有函數

在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_pyhello_javaPY 是提供給外部使用的,而_hello 只建議在模塊內部使用。

其實在Python 中的這種訪問權限的控制,其實是「假的」,由於從技術上來講,這種方式,並不能徹底的禁止外部調用,只是至關於一種編程規範

若是咱們非要使用這種以下劃線開頭的函數,從技術上來講也是行得通的,以下:

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

但通常咱們並不建議這樣作。

5,__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__': 內的代碼塊並不會被執行。

6,Python 包

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 兩個元素,因此只導入了這兩個標識符。

7,模塊的導入路徑

從內建模塊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 目錄appendsys.path 中,而後再import

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

(完。)


推薦閱讀:

Python 簡明教程 --- 12,Python 字典

Python 簡明教程 --- 13,Python 集合

Python 簡明教程 --- 14,Python 數據結構進階

Python 簡明教程 --- 15,Python 函數

Python 簡明教程 --- 16,Python 高階函數


歡迎關注做者公衆號,獲取更多技術乾貨。

碼農充電站pro

相關文章
相關標籤/搜索