Python 模塊

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. 函數形式
      將導入的句式放入函數體代碼 等待所有的名字加載完畢之後再調用
Tags: