模塊

模塊就是完成某項功能的程序集,比如 sys 模塊,random 模塊等。在 Python 語言中,每個 .py 文件就是一個模塊。

創建一個模塊

任何以 .py 結尾的文件都是一個模塊。
創建 tool.py 文件:

def greeting(name):
    print("Hello, I'm %s"%name)

def add(a,b):
    return a+b

在 py.py 中使用該模塊:

import tool
tool.greeting("Mike")

運行結果:

Hello, I'm Mike

引入模塊的幾種方式

以上面創建的 tool 模塊為例,有一下幾種引入方式(1 為引入方式,2 為調用方式):

1.import tool
2.tool.greeting("Mike")
1.from tool import greeting,add
2.greeting("Mike")
1.from tool import *
2.greeting("Mike")

__all__ 變量

默認情況下,使用 from xxx import * 導入模塊時會導入模塊中所有的函數和數據,如果我們想限定哪些內容能被導入,哪些內容不能被導入,就需要使用 __all__ 變量,該變量是系統內置的,默認為一個空列表。我們可以在該列表中添加想要被導出的內容。修改 tool.py:

__all__ = ["greeting"]

def greeting(name):
    print("Hello, I'm %s"%name)

def add(a,b):
    return a+b

在 py.py 中調用 add 方法試試:

from tool import *

greeting("Mike")
print(add(1,2))

運行結果如下:

Hello, I'm Mike
Traceback (most recent call last):
  File "C:\Users\Charley\Desktop\py\py.py", line 4, in <module>
    print(add(1,2))
NameError: name 'add' is not defined

greeting 函數被成功執行了,而 add 函數沒有找到。
對于模塊內部的私有變量或者函數,還可以在名稱前加上一個下劃線 _,這樣就不會被導出了。
注:這種方式只對 from xxx import * 有效:
tool.py:

_num = 1

def count():
    global _num
    _num += 1
    return _num

py.py:

from tool import *
print(count())
print(_num)

運行結果如下:

2
Traceback (most recent call last):
  File "C:\Users\Charley\Desktop\py\py.py", line 3, in <module>
    print(_num)
NameError: name '_num' is not defined
[Finished in 0.1s with exit code 1]
[shell_cmd: python -u "C:\Users\Charley\Desktop\py\py.py"]

這里無法使用 _num 變量。以上的方式只對 from xxx import * 有效,如果我們不使用星號 * 引入,則無此限制:

from tool import count,_num
print(count())
print(_num)

運行結果為:

2
1

__pycache__ 文件夾

在導入 tool 模塊后運行代碼,發現多出了一個 __pycache__ 文件夾:

│  py.py
│  tool.py
│
└─__pycache__
        tool.cpython-35.pyc

其中有個 .pyc 文件,這是編譯器為了提高運行速度創建的緩存字節碼文件,這里有一個簡要介紹:

如果 Python 進程在機器上擁有寫入權限,那么它將把程序的字節碼保存為一個以 .pyc 為擴展名的文件( ".pyc" 就是編譯過的 ".py" 源代碼)。當程序運行之后,你會在那些源代碼的附近(也就是說同一個目錄下)看到這些文件。
Python這樣保存字節碼是作為一種啟動速度的優化。下一次運行程序時,如果你在上次保存字節碼之后沒有修改過源代碼的話,Python將會加載.pyc文件并跳過編譯這個步驟。當Python必須重編譯時,它會自動檢查源文件和字節碼文件的時間戳:如果你又保存了源代碼,下次程序運行時,字節碼將自動重新創建。

上面的解釋來自于《python學習手冊》。
總是,__pycache__ 和其下的 .pyc 文件是解釋器為了優化代碼執行創建的,并且只會創建被導入的模塊緩存,主模塊不創建緩存。

有時候一個模塊無法完成具體的功能,需要多個模塊協作完成,我們可以把這些模塊放在一個文件夾中進行統一管理,這就是 Python 中包的概念。
Python3 和 Python2 對于包的概念有點小小的區別:Python3 認為一個文件夾就是一個包,Python2 認為帶有 __init__.py 文件的文件夾是一個包。但不管是 Python3 還是 Python3,如果包中有 __init__ 文件,在導入包的時候都會先執行 __init.py__ 文件。
Python3 下可以直接導入包中的模塊:

from mypkg import tool
tool.greeting("NAME")

運行結果:

Hello, I'm NAME

Python2 下需要新建 __init__.py 文件:

__all__ = ["tool"]

除此之外使用方式和 Python3 一樣:

from mypkg import tool
tool.greeting("Python2 中使用")

為了兼容性,建議總是在包中加上 __init__.py 文件。

模塊尋找的路徑

sys 模塊中的 path 變量可以幫助我們查看 Python 尋找模塊的路徑:

import sys
print(sys.path)

運行結果:


[
'C:\\Users\\Charley\\Desktop\\py', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\python35.zip', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\DLLs', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib\\site-packages'
]

在尋找模塊時,會依次按照列表中的路徑尋找,直到找到位置。
如果我們想自定義尋找路徑,可以修改 path 變量:

from sys import path
path.append("./mypkg/");

from tool import greeting
greeting("Mike")

運行結果為:

Hello, I'm Mike
[
'C:\\Users\\Charley\\Desktop\\py', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\python35.zip', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\DLLs', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib\\site-packages',
'./mypkg/'
]

我們在 path 變量中增加了一條路徑,定義到前面建立的包中,這樣就可以直接導入包中的模塊,不需要導入包名了。這只是一種展示性的做法,不推薦使用。

模塊熱導入

這是我自己編的一個名詞,意思是導入模塊后,程序在不退出的前提下,如果修改了被導入的模塊,可以同步使用修改后的模塊中的功能。
創建一個 tesereload.py 模塊:

def show():
    print("Hello World")
    # print("Hello China")

打開命令行,導入 testreload 模塊,并執行 show 函數:

>>> import testreload
>>> testreload.show()
Hello World

修改 testreload 模塊,取消注釋,再次在命令行中執行 show 函數:

>>> testreload.show()
Hello World

在命令行中使用 import 再次導入 testreload 模塊,再執行 show 函數:

>>> import testreload
>>> testreload.show()
Hello World

執行結果并沒有發生變化,這是因為前面講到的緩存機制,在程序運行起來之后,修改了原始模塊并不會同步更新導入,而是從緩存的 pyc 文件中讀取。如果需要重新應用修改后的模塊,可以:

  • 重新啟動程序
  • 使用 imp 模塊中的 reload 方法

繼續在命令行中導入 imp 模塊,并通過 reload 方法重新載入模塊:

>>> from imp import reload
>>> reload(testreload)
<module 'testreload' from 'C:\\Users\\Charley\\Desktop\\py\\testreload.py'>

再次執行 show 方法:

>>> testreload.show()
Hello World
Hello China

可見 show 方法的執行結果已經同步更新了。這就是 reload 的作用。

避免模塊循環導入

模塊循環導入就是兩個模塊之間相互依賴導入,會產生死循環,修改 py.py 文件:

from testreload import show

def show2():
    pass
show()

修改 testreload.py 文件:

from py import show2

def show():
    print("Hello World")

show2()

運行 py.py 文件:

Traceback (most recent call last):
  File "C:\Users\Charley\Desktop\py\py.py", line 1, in <module>
    from testreload import show
  File "C:\Users\Charley\Desktop\py\testreload.py", line 1, in <module>
    from py import show2
  File "C:\Users\Charley\Desktop\py\py.py", line 1, in <module>
    from testreload import show
ImportError: cannot import name 'show'
[Finished in 0.2s with exit code 1]
[shell_cmd: python -u "C:\Users\Charley\Desktop\py\py.py"]
[dir: C:\Users\Charley\Desktop\py]
[path: C:\Python27\;C:\Python27\Scripts;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;D:\nvm;D:\nodejs;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\ProgramData\chocolatey\bin;C:\Program Files (x86)\GtkSharp\2.12\bin;C:\Program Files\MySQL\MySQL Utilities 1.6\;C:\Users\Charley\AppData\Local\Programs\Python\Python35-32\Scripts\;C:\Users\Charley\AppData\Local\Programs\Python\Python35-32\;D:\Git\bin;D:\Sublime Text 3;D:\MinGW\bin\;D:\MinGW\bin\;D:\Microsoft VS Code\bin;D:\Java\jdk 8.0\bin;D:\Android\sdk;C:\Program Files\MySQL\MySQL Server 5.7\bin;]

程序直接掛掉了。因此我們需要避免模塊的循環導入,若兩個模塊相互有依賴,應該通過第三個的抽象進行解決。

__name__ 變量

__name__ 變量是系統提供的一個全局變量,用來對當前的模塊進行描述。我們在 py.py 文件和 testreload.py 文件中分別查看 __name__ 變量:
py.py:

import testreload
print("--> py %s"%__name__)

testreload.py:

print("--> testreload %s"%__name__)

運行 py.py 文件:

--> testreload testreload
--> py __main__

__name__ 變量用來對當前的模塊進行描述,如果是主模塊,其值為 __main__,如果是被導入的模塊,其值就是模塊名。
實際應用中,我們可以根據 __name__ 變量來決定是否執行模塊中的內容。修改 testreload.py:

print("--> testreload %s"%__name__)

if __name__ == "__main__":
    print("__MAIN__")

運行 py.py 文件:

--> testreload testreload
--> py __main__

運行 testreload.py 文件:

--> testreload __main__
__MAIN__

在 testreload 模塊中,只有該模塊是組模塊中才執行 print 函數輸出。
項目中我們一般有一個入口文件,建議在入口文件中對 __name__ 進行判斷,完成初始化工作:

# 如果當前模塊是主模塊
if __name__ == "__main__":
    # 執行初始化函數
    main()

完。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 用 python 解釋器來編程從 Python 解釋器退出再進入,那么你定義的所有的方法和變量就都消失了。 為此...
    chen_000閱讀 539評論 0 3
  • 模塊簡介 在軟件開發過程中,隨著代碼的不斷增加,在一個問價里代碼就會越來越長,不容易維護。為了編寫可維護的代碼,我...
    齊天大圣李圣杰閱讀 815評論 0 0
  • 模塊是最高級別的程序組織單元,它將程序代碼和數據封裝起來以便重用。 模塊是變量名的封裝,被認為是命名空間。模塊導入...
    lakerszhy閱讀 536評論 0 2
  • 課程概要:1、認識Python 模塊2、字節編譯3、from … import 詳解4、認識 name 屬性5、自...
    LuCh1Monster閱讀 775評論 0 7
  • 目錄: 上一節課生成器還有一些知識點沒講到,接下來補充; 一、協程函數 生成器:yield關鍵字的另外一種用法yi...
    CaiGuangyin閱讀 611評論 0 1