Python 模組

Hans_Wang發表於2021-11-23

Python 模組

image

1. 模組簡介

1.1 什麼是模組

編寫較長程式時,建議用文字編輯器代替直譯器,執行檔案中的輸入內容,這就是編寫 指令碼 。隨著程式越來越長,為了方便維護,最好把指令碼拆分成多個檔案。編寫指令碼還一個好處,不同程式呼叫同一個函式時,不用每次把函式複製到各個程式。為實現這些需求,Python 把各種定義存入一個檔案,在指令碼或直譯器的互動式例項中使用。這個檔案就是 模組

模組即是一系列功能的結合體。

1.2 為什麼使用模組

提高了程式碼的可維護性,不用重複造輪子提升開發效率.

1.3 模組的各類

  1. 內建模組(使用python直譯器直接可以匯入)
  2. 第三方模組(使用pip安裝)
  3. 自定義

1.4 模組的表現形式

  • 使用python編寫的程式碼(.py檔案),就是平時寫的一個python檔案

  • 已被編譯為共享庫或DLL的C或C++擴充套件

  • 包好一組模組的包(資料夾)

    包其實就是多個py檔案(模組)的集合

    包裡面通常會含有一個__init__.py檔案(在python3中這個檔案可以沒有)

  • 使用C編寫並連結到python直譯器的內建模組

2. import句式

匯入模組使用關鍵字importpy檔名,不要加.py.

示例:

# 匯入內建模組
>>> import time
>>> time.time()  # 直接使用
1637651203.9467623
#匯入自定義
# 程式碼檔案:foo.py 
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

# 匯入    
>>> import foo
>>> foo.
foo.hello(  foo.name    
>>> foo.name
'Hans'
>>> foo.hello(foo.name)
Hello, Hans
>>> foo.hello("Jack")  
Hello, Jack
          
# 同一個模組多次匯入
# 程式碼檔案:boo.py
print("hello")

# 匯入        
>>> import boo  # 第一次匯入,會執行裡面的程式碼。
hello
>>> import boo		# 第二次匯入,不會執行
>>> import boo		# 第二次匯入,不會執行
>>> import boo		# 第二次匯入,不會執行
# 多次匯入相同模組 只會執行一次

模組首次匯入發生了什麼?(以匯入boo.py中匯入foo.py為例)

# foo.py
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

# boo.py 
import foo
print("hello world")

foo.name
foo.hello(foo.name)
foo.hello("Jack")

# 執行結果:
hello world
Hello, Hans
Hello, Jack
  1. 執行匯入檔案(boo.py)產生該檔案的全域性名稱空間
  2. 執行foo.py
  3. 產生foo.py全域性名稱空間 執行foo.py檔案內程式碼 將產生的名字全部存檔於foo.py名稱空間
  4. 在匯入檔名稱空間產生一個foo的名字指向foo.py全域性名稱空間

image

import方法匯入模組後就可以使用模組中的所有的變數名或函式名,而且絕對不會衝突,因為呼叫的時候已經指定了要使用哪個包中的那個變數或函式

3. from...import...句式

from...import...句式為從哪個包或模組中匯入哪個模組或功能。

示例:

# foo.py程式碼:
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

def hi():
    print("Hi, world")
    
# boo.py程式碼:    
from foo import hi  #在boo中只使用foo的hi功能
print("hello world")

hi()

# 執行結果:
hello world
Hi, world

# 程式碼 boo.py 
from foo import hi
from foo import hi
from foo import hi
執行結果:
from foo
# from...import...多次匯入也只會匯入一次

使用from...import...匯入:

  1. 先產生執行檔案的全域性名稱空間
  2. 執行模組檔案 產生模組的全域性名稱空間
  3. 將模組中執行之後產生的名字全部存檔於模組名稱空間中
  4. 在執行檔案中有一個hi執行模組名稱空間中hi指向的值

匯入

# foo.py 程式碼
print("from foo")
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

def hi():
    print("Hi, world")

# boo.py 程式碼
    
from foo import hi
print("hello world")

def hi():
    print("from boo hi")

hi()
# 執行結果:
from foo
hello world
from boo hi   # 發現執行hi()的結果為boo.py中的函式不是從foo.py中匯入進來的hi

from...import...指定的匯入某個名字

在使用的時候直接寫名字即可 但是當前名稱空間有相同名字的時候,就會產生衝突 使用的就變成了當前名稱空間

4. 匯入方式的擴充套件

4.1 使用別名

# import匯入
>>> import foo as f  # 把foo定義別名為f,這時只能呼叫f,如果再呼叫foo就會報錯,說foo沒有定義
>>> f.name
'Hans'

# from ... import ...匯入
>>> from foo import hi as h		# 把hi定義為別名為h,呼叫的時候直接使用h即可,同理hi也不能使用
>>> h()
Hi, world

4.2 連續匯入

# import匯入
>>> import sys
>>> import os
# 上面的匯入方式可以寫成下面:
>>> import sys, os  # 這種方式和上面的方式功能是一樣的

# from ... import ...匯入
>>> from foo import hello
>>> from foo import hi
# 上面的匯入方式可以寫成下面:
>>> from foo import hello, hi  # 這種方式和上面的方式功能是一樣的

import使用連續匯入多個模組時,如果多個模組功能相似或者屬於同一個系列時推薦使用。

如果功能不同並且不屬於一個系列 那麼推薦分行匯入

4.3 通用匯入

如果使用from ... import ...方式匯入一個模組裡全部功能時,最基本的方法是依次匯入
>>> from foo import hello, hi, name 
# 或
>>> from foo import hello
>>> from foo import hi
>>> from foo import name 

#可以使用* 號把一個模組裡的全部功能都匯入
>>> from foo import * 


# 如果一個模組裡有三個功能,在使用from ... import ... 想讓人用其中兩個可以使用__all__
# 程式碼:
print("from foo")
name = 'Hans'

def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

__all__ = ['hi', 'play']  # 在被匯入的模組檔案中可以使用__all__指定可以被匯入使用的名字

# 執行:
>>> from foo import *
from foo
>>> hi()
from foo Hi
>>> play()
from foo play
>>> hello("Hans")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'hello' is not define

4.4 判斷py檔案是作為模組檔案還是執行檔案

可以使用函式自帶的__name__方法

# foo.py 程式碼 
print("from foo")
name = 'Hans'

def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

print("__name__: %s" % __name__)

# 執行如果:
from foo
__name__: __main__
# 如果foo.py是直接執行時。__name__為 __main__  

# 如果foo.py 當成模組在別的檔案裡匯入時:
# importTest.py  程式碼
import foo

#執行結果:
from foo
__name__: foo   
# 如果foo.py檔案是被當做模組匯入則返回模組名

# 
# 一個py檔案當成模組被匯入時,它會直接執行py檔案裡的全部程式碼,可以利用__name__來判斷它是否被當成模組匯入,如果是則不執行
# 程式碼 foo.py
def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

if __name__ == '__main__':
    print("from foo")
    name = 'Hans'
# 程式碼 importTest.py 
import foo
# 執行結果:

#之前匯入的時候會直接列印: from foo

5. 模組匯入的順序

模組匯入的順序:

  1. 先從記憶體中查詢
  2. 再去內建模組中查詢
  3. 最後去sys.path系統路徑查詢(自定義模組)

如果都沒有查詢到則報錯

# 1.在記憶體中查詢
# foo.py 程式碼:

def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

if __name__ == '__main__':
    print("from foo")
    name = 'Hans'
# importTest.py 程式碼:

from foo import hello
import time 
print("Hello")
time.sleep(10)
hello("time")

# 執行結果:
Hello
				#在time.sleep(10)的時候把foo.py刪除,這時foo.py已經載入到記憶體中,所以下面依然執行
from foo. Hello, time

#如果再執行則會報錯

# 2.再去內建模組中查詢
# 可以自己定義一個和內建模組同名的模組,看看匯入的是誰

# 自己編寫:time.py 程式碼:
print("time...")

# boo.py 程式碼: 
import time
print(time)

# 執行結果:   
<module 'time' (built-in)>
# 發現time為內建的模組,所以在給py檔案命名的時候不要與內建模組名衝突

# sys.path系統路徑查詢
>>> import sys
>>> sys.path  
['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/local/lib64/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages/cloud_init-17.1-py3.6.egg', '/usr/lib/python3.6/site-packages', '/usr/lib64/python3.6/site-packages']

# ''為當前目錄,然後依次查詢

當某個自定義模組查詢不到的時候解決方案:

  1. 自己手動將該模組所在的路徑新增到sys.path中

    # 檢視當前目錄
    [root@hans_tencent_centos82 tmp]# pwd
    /tmp
    [root@hans_tencent_centos82 tmp]# python3
    >>> import foo
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ModuleNotFoundError: No module named 'foo'
    # 提示沒有foo模組。
    # 查詢foo.py模組在哪
    [root@hans_tencent_centos82 module]# pwd
    /tmp/module
    [root@hans_tencent_centos82 module]# ls -lrt foo.py 
    -rw-r--r-- 1 root root 202 Nov 23 16:54 foo.py
    # foo.py在/tmp/module目錄下。
    # /tmp/module加入到sys.path
    >>> import sys
    >>> sys.path.append('/tmp/module')
    >>> import foo
    >>> sys.path
    ['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/local/lib64/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages/cloud_init-17.1-py3.6.egg', '/usr/lib/python3.6/site-packages', '/usr/lib64/python3.6/site-packages', '/tmp/module']
    # 可以看到 '/tmp/module'新增到sys.path
    
  2. 使用from...import...句式

    from 資料夾名稱.資料夾名稱 import 模組名
    from 資料夾名稱.模組名稱 import 名字

    # from 資料夾名稱.資料夾名稱 import 模組名
    
    #  foo.py在/tmp/module目錄下。
    # 當前目錄為/tmp
    # 使用from...import...
    # 執行結果:
    >>> from module import foo
    >>> foo.hi()
    from foo Hi
    
    #當前在/tmp下,而foo.py在/tmp/module/test/下
    [root@hans_tencent_centos82 tmp]# ls -lrt /tmp/module/test/foo.py 
    -rw-r--r-- 1 root root 202 Nov 23 16:54 /tmp/module/test/foo.py
    >>> from module.test import foo
    >>> foo.play()
    from foo play
    
    # from 資料夾名稱.模組名稱 import 名字
    
    #只匯入foo模組中的一個功能:
    >>> from module.test.foo import play
    >>> play()
    from foo play
    

    6. 迴圈匯入

    不允許出現迴圈匯入

    真要出現了,一般解決方法(就是明知道有迴圈匯入了還是讓它執行,一錯再錯方法):

    1. 調換順序
      將彼此匯入的句式放在程式碼的最後
    2. 函式形式
      將匯入的句式放入函式體程式碼 等待所有的名字載入完畢之後再呼叫

相關文章