python可重用結構——模塊

一、概述

python可重用模塊實際上就是,包含函數或者類的python腳本,對於一個大型的腳本而言,經常需要將其功能細化,將實現不同功能的代碼放在不同的腳本中實現,在其他的腳本中以模塊的形式,實現細化的功能,以便腳本的維護和重用。

二、python模塊的基本用法

模塊是包含函數和其他語句的python腳本文件,它以“.py”爲後綴名,用作模塊的python腳本與其他腳本,並沒有什麼區別。

2.1 導入模塊

在python中可以使用以下兩種方法導入模塊和模塊中的函數

  • import 模塊名
  • import 模塊名as新名字
  • from 模塊名import函數名
           其中,使用import是將整個模塊導入,而使用from則是將模塊中某一個函數或者名字導入,而不是整個模塊。使用import和from導入模塊還有一個不同:要想使用import導入模塊中的函數,則必須以 " 模塊名+ " . " +函數名" 的形式調用函數;而要想使用from導入模塊中的某個函數,則可以直接使用函數名調用,不必在前面加上模塊名稱。
代碼:
import math   # 使用import導入math模塊
a = math.sqrt(9)   # 使用模塊中的sqrt函數
print(a)
執行結果:
3.0

代碼:
sqrt(9)       # 直接使用sqrt函數,調用失敗
執行結果:
NameError: name 'sqrt' is not defined

代碼:
from math import sqrt  # 使用from導入math模塊中的sqrt函數
b = sqrt(9) # 直接使用函數名,調用函數
print(b)
執行結果:
3.0

使用from導入模塊中的函數,調用是不再需要函數名,如果想將模塊中的所有函數都使用這種方式導入,可以在from中使用“*”通配符,表示將模塊中的所有函數導入。

代碼:
from math import *
a = sqrt(9)  # 求9的平方根
b = sin(30) # 求x的正弦
print( "%.1f\n %.1f\n" %(a,b))
執行結果:
3.0
-1.0

在python2.x中,還可以通過內置函數reload重新載入模塊,reload可以在模塊被修改的情況下不必關閉python而重新載入模塊,而使用reload重載模塊時,該模塊必須事先已經被導入。
在python3中,reload函數已經被刪除,而重新載入模塊,則需要使用imp模塊中的reload函數

import os
import imp
imp.reload(os)

2.2 編寫一個簡單的模塊

mymodule.py文件內容:

def show():
    print("this is mymodule")
    
test.py文件內容:

import mymodule
mymodule.show()

執行test.py 結果:
this is mymodule

在模塊中除了定義函數外還可以定義變量,變量同樣可以在其他腳本中使用。

模塊定義腳本中,添加一個變量
def show():
    print("this is mymodule")
name = 'mymodule'
測試腳本:
import mymodule
mymodule.show()
print(mymodule.name) # 打印腳本
mymodule.name = 'change' # 重新賦值
print(mymodule.name)
執行結果:
this is mymodule
mymodule
change

三、在哪裏查找模塊

      編寫好的模塊只有被 Python 找到才能被導入。上一節中編寫的模塊和調用模塊的腳本位於同一個目錄中,因此不需要進行設置就能被Python 找到並導入。如果在該目錄中新建一 個module目錄,並且把mymodule,py 轉移到mymodule 目錄中,
在這裏插入圖片描述
再次在Windows 的命令窗口中運行usemodule,py,則輸出如下:
Traceback (most recent call last): File "E:/python練習/test.py", line 8, in <module> from venv.mymodule import mymodule ModuleNotFoundError: No module named 'venv.mymodule'
      運行腳本出錯,Python 解釋器沒有找到mymodule模塊。在導入模塊時,Python 解釋器首先在當前目錄中查找要導入的模塊。如果未找到模塊,則Python解釋器會從sys模塊中path變量指定的目錄中查找導入模塊。如果在以上所有目錄中都未找到導入的模塊,則會輸出出錯信息。
以下代碼是使用sys path輸出Pythion 的模塊查找路徑:

import sys
print(sys.path)

path路徑:
['E:\\python練習', 
'E:\\python練習', 
'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32\\python37.zip', 
'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32\\DLLs', 
'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32\\lib', 
'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32', 
'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages', 
'E:\\軟件\\pycharm\\PyCharm 2018.3.4\\helpers\\pycharm_matplotlib_backend']

     在腳本中可以向sys.path中添加模塊查找路徑,以下所示腳本中,將當前目錄下的mymodule子目錄添加到sys.path中,並從module目錄中導入mymodule模塊,代碼如下:

修改test代碼:
import os
import sys
mymodulepath = os.path.abspath(os.path.dirname(os.getcwd()))+'\\mymodule'   # 獲取mymodule.py所在目錄
print(mymodulepath)
sys.path.append(mymodulepath)   # 添加path路徑
print(sys.path  )
import mymodule
mymodule.show()
print(mymodule.name)
mymodule.name = 'change'
print(mymodule.name)
執行結果:
E:\python練習\venv\mymodule   
['E:\\python練習\\venv\\Scripts',
'E:\\軟件\\pycharm\\PyCharm 2018.3.4\\helpers\\pydev',
'E:\\python練習',
 'E:\\軟件\\pycharm\\PyCharm 2018.3.4\\helpers\\third_party\\thriftpy', '
 E:\\軟件\\pycharm\\PyCharm 2018.3.4\\helpers\\pydev', 'C:\\Users\\lenovo\\.PyCharm2018.3\\system\\cythonExtensions', 'E:\\python練習\\venv\\Scripts', 'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32\\python37.zip', 'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32\\DLLs', 'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32\\lib', 'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32', 'C:\\Users\\lenovo\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages',
'E:\\軟件\\pycharm\\PyCharm 2018.3.4\\helpers\\pycharm_matplotlib_backend',
'E:\\python練習\\venv\\mymodule']
E:\python練習\venv\mymodule    # 怎加的path路徑
E:\python練習\venv\Scripts
E:\python練習\venv     
this is mymodule
mymodule
change

關於window下獲取目錄可以參照這篇博客:博客鏈接

代碼:
import os
print(os.path.abspath(__file__))  # 返回當前的絕對路徑
print(os.getcwd()) # 返回當前所在目錄
print(os.path.abspath(os.path.join(os.getcwd(),"..")))  # 從裏往外看-->(返回當前目錄,".."上級目錄)os.join結合起來,在用os.path.abpath返回絕對路徑。S
print(os.path.dirname(os.getcwd())) # os.path.dirname當前路徑的目錄
print(os.path.abspath(os.path.join(os.getcwd(),"../.."))) # 返回上上級目錄
print(os.path.dirname(os.path.dirname(os.getcwd())))  # 兩次嵌套
執行結果:
E:\python練習\venv
E:\python練習\venv
E:\python練習
E:\python練習
  • 在上一節的例子中,在python2運行完test.py會發現,moudle目錄中除了mymodule,py文件外還多了一個mymodule.pyc文件。其實,mymodule.pyc就是Python將mymodule.py編譯成字節碼的文件。雖然Python是腳本語言,但Python可以將腳本編譯成字節碼的形式。對於模塊,Python總是在第一次調用後就將其編譯成字節碼的形式,以提高腳本的啓動速度。Ppthon在導入模塊時會查找模塊的字節碼文件,如果存在則將編譯後的模塊的修改時間同模塊的修改時間相比較。如果兩者的修改時間不相符,則Pyhton將重新編譯模塊,以保證兩者相符。被編譯的腳本也是可以直接運行的。
  • 在Python3中,如果在py_ compile.compile函數中不指定第2個參數,則將在當前目錄新建一個 名爲“__pycache__'的目錄,並在這個目錄中生成“被編譯模塊名.cpython-37.pyc"的pytc字節碼文件.
    在這裏插入圖片描述
  • 當然,沒有必要去刻意編譯Python腳本。不過,由於Python是腳本,如果不想將淚則可以發佈編譯後的腳本,這樣能起到定的保護源文件的作用。對於非模塊的腳本,Python 不會在運行腳本後將其編譯成字節碼的形式。可以使用compile模塊。以下代碼可以將其中的mymodule.py文件編譯成.pyc文件。
# file compile.py 和mymodule在同一個目錄下
import py_compile
py_compile.compile("mymodule.py","mymodule.pyc" )

在這裏插入圖片描述
另外,還可以通過Python的命令行選項將腳本優化編譯。Python編譯的優化選項有以下兩個。

◆-O 該選項對腳本的優化不多,編譯後的腳本以“.pyo"爲擴展名。凡是以“.pyo"
爲擴展名的Python字節碼都是經過優化的。
◆-OO 該選項對腳本優化的程度較大。使用該標誌可以使編譯的Python腳本更小。使
用該選項可能導致腳本運行錯誤,因此,應謹慎使用。
可以通過在命令行中輸入以下命令將mymodule.py優化編譯。

python -O mymodule.py
python -OO mymodule.py

四、模塊也可以獨立運行

每個Python腳本在運行時都有一個__name__ 屬性 ( name前後均是兩條下畫線)。在腳本中通過對name 屬性值的判斷,可以讓腳本在作爲導入模塊和獨立運行時都可以正確運行。在Python中,如果腳本作爲模塊被導入,則其__name__ 屬性被設置爲模塊名; 如果腳本獨立運行,則其__name__ 屬性被設置爲 __main__ 。 因此可以通過__name__ 屬性 來判斷腳本的運行狀態。

五、如何查看模塊提供的函數名

如果想查看模塊提供的函數名,可以通過內置函數dir()來進行操作,以下代碼獲得sys模塊中的函數名:

>>> import sys
>>> dir(sys)
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', 
'__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', 
'_current_frames', '_debugmallocstats', '_enablelegacywindowsfsencoding', '_framework', '_getframe', '_git', '_home', 
'_xoptions', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 'builtin_module_names', 
'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 
'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 
'get_coroutine_origin_tracking_depth', 'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', 
'getdefaultencoding', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 
'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 'hash_info', 'hexversion', 'implementation', 'int_info', 
'intern', 'is_finalizing', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 
'platform', 'prefix', 'ps1', 'ps2', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'set_coroutine_wrapper', 
'setcheckinterval', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 
'version', 'version_info', 'warnoptions', 'winver']
>>>

dir函數原型如下:
dir ( [object] )
其參數含義如下:
object 可選參數,要列舉的模塊名
如果不向dir()函數傳遞參數,那麼dir()函數將返回當前腳本所在的所有名字列表


>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'sys']
>>> a = [ 1,2,3]
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'sys']

六、用包來管理多個模塊

  • 在java中,通過包將比不同的類組織在一起,類似的,在python中也提供了包的功能,可以使用包來管理多個模塊。使用包的好處在於可以有效避免名字衝突,便於包的維護管理。Python 中的模塊包可以通過路徑導入模塊。
  • 包的組成:
    包可以看作處於同一目錄中的模塊。在Python中首先使用目錄名,然後再使用模塊名導入所需要的模塊。在包的每個目錄中都必須包含一個名爲__init__ py ( init的前後均是兩條下畫線)的文件。__init__ py 可以是一個空文件,僅用於表示該目錄是一個包。__init__ py 的主要用途是設置__all__ 變量以及所包含的包初始化所需的代碼。 對於在from中使用 “*”通配符導入包內所有名字時,在__init__ py 中設置__all__ 變量可以保證名字的正確導入。

一個簡單的python包的目錄組成如圖所示:
在這裏插入圖片描述
如果需要導入B目錄中的a.py模塊,可以使用以下語句之一:

from A.B import a
import A.B.a
  • 有了包的概念就可以很好地解決模塊查找路徑的問題。只要將所有的模塊放在當前目錄中的某一文件夾內, 然後在該文件夾中新建一個空的__init__.py文件,就可以通過目錄結構的層次導人所需的模塊。而不必像前邊的例子那樣將子目錄的路徑添加到sys.path列表中。
包的內部引用
  • Python包中的模塊也可能需要相互引用。對於上圖所示的位於C目錄中的b.py,如果要引用同樣位於C目錄中的a.py,則可以使用以下語句。
    import a
    如果位於C目錄中的b.py,想要引用位於B目錄中的a.py,則需要使用以下語句。
    from A.B import a
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章