模塊

1.模塊介紹

1.1 模塊的概念

模塊是什麼 ?python

模塊就是一系列功能(函數)的集合體程序員

模塊的分類bootstrap

  • 內置模塊 c語言寫的模塊
  • 第三方模塊 別人寫好的模塊
  • 自定義模塊 本身自定義的

一個python文件自己就是一個模塊 , 文件名m.py , 模塊名叫mdom

ps:模塊分爲4中形式
    1.使用python編寫的.py文件
    2.已被編譯爲共享庫或DLL的C或C++擴展
    3.把一系列模塊組織到一塊兒的文件夾(注:文件夾下有一個__init__.py文件,該文件夾稱之爲包
    4.使用C編寫並連接到python解釋器的內置模塊

1.2 爲何要用模塊

  1. 內置與第三方的模塊拿來就用,無需定義,這種拿來主義,能夠極大的提高本身的開發效率
  2. 能夠將程序的各部分功能提取出來放到一模塊中爲你們共享使用
  3. 減小了代碼冗餘,程序組織結構更加清晰

2. 模塊的兩種導入方式

2.1 import

[建立模塊]函數

# m.py
print('======m=====')
x = 100
def get():
    print("get ========%s" % x)


def change():
    global x
    x += 1
    return x

[導入模塊]性能

# demo.py
import m     # 導入m模塊

x = 1
y = 2

# ======m=====  
# 打印l ======m===== ,說明導入模塊,會運行模塊裏面的代碼

[思考] 首次模塊會發生什麼事情測試

1.產生m.py的名稱空間
2.執行m.py代碼,將m.py運行過程當中產生的名字都丟到m的名稱空間
3.在當前文件中產生一個名字m,指向1中產生的名稱空間
image-20201211193721713

[以後導入]優化

以後的導入,都是直接引用第一次導入產生的m.py名稱空間,不會重複執行代碼ui

[深刻理解]設計

# demo.py
import m

print(m.x)            # 100
print(m.get)          # <function get at 0x000001FD80567620>
print(m.change)       # <function change at 0x000001FD81616510>

[強調]

  1. 模塊名.名字,是指名道姓的問某一個模塊要名字對應的值,不會與當前名稱空間的名字發生衝突
  2. 不管是查看仍是修改操做的都是以定義階段爲準(原模塊自己),與調用位置無關
# demo.py
import m

x = 333
m.get()     # get ======== 100

m.change()  # 改變的是m名稱空間裏面的x值
print(x)    # 333

print(m.x)  # 101

[補充]

# 1.能夠以逗號爲分隔符在一行導入多個模塊
# 建議以下所示導入多個模塊,層次清晰
import time
import random
import m

# 不建議在一行同時導入多個模塊
import time, random, m

# 2.導入模塊的規範

# 1.python內置模塊
# 2.第三方模塊
# 3.程序員自定義模塊

import time
import sys
import 第三方模塊1
import 第三方模塊2
import 自定義模塊1
import 自定義模塊1

# 3.import ... as ...
# 爲導入的模塊起一個別名,之後能夠經過別名.名字,取到模塊裏面的名字
# 適用於比較長的第三方模塊

import jhcsdfveubvf as f

# 4.模塊是第一類對象,和變量名,函數名同樣,能夠被賦值,能夠當參數,返回值,容器的元素

# 5.自定義模塊的命名應該採用純小寫+下劃線風格,py2中仍是駝峯體

# 6.能夠在函數中導入模塊

2.2 from ... import ....

# foo.py
print('======foo=====')
x = 0
def get():
    print("get ========%s" % x)


def change():
    global x
    x += 1
    return x
# run.py

from foo import X          # x=模塊foo中值0的內存地址
from foo import get
from foo import change

print(x)                   # 0
print(get)                 # <function get at 0x0000029FBD8E7620>
print(change)              # <function change at 0x0000029FBE996510>

# 注意:x,get,change這三個名字是在run的名稱空間中,可是指向的都是foo裏面值的內存地址,並非名字
# 只是他們的名字相同又指向相同的內存地址,可是不在一個名稱空間,因此不會相互影響
image-20201212124645778

[導入模塊發生的事]

# 一、產一個模塊的名稱空間
# 二、運行foo.py將運行過程當中產生的名字都丟到模塊的名稱空間去
# 三、在當前名稱空間拿到一個名字,該名字指向模塊名稱空間中的某一個內存地址

[強調]

# run.py

from foo import X          # x=模塊foo中值0的內存地址
from foo import get
from foo import change

print(x)                   # 0
print(get)                 # <function get at 0x0000029FBD8E7620>
print(change)              # <function change at 0x0000029FBE996510>

x = 3333       # 改變全局名稱空間中x的指向,如今x指向值3333的內存地址
print(x)       # 3333

get()          # 仍然打印x=0,由於get中的x是引用foo名稱空間中x指向的值,foo中x的指向並無變
change()       # 改變foo中x的內存地址的指向
get()          # 打印1

print(x)       # 雖然foo中x的指向發送改變了,可是run中的這個x並不會發生變化,仍是原來的內存地址0

from foo import x   # 這個時候x就指向,foo裏面x指向的新地址了
print(x)            # 打印 1

[補充]

#一行導入多個名字(不推薦)
from foo import X, get,change

# *:導入模塊中的全部名字,當用到一個模塊中超級多的名字時,能夠用*,可是最好也要規避掉重名的問題
from foo import *|
print(x)

# 起別名
from foo import get as g    # 針對get起了一個別名g

[瞭解]

# 瞭解 : __all__ = ["名字1","名字2",.....],列表的元素全是字符串格式的名字
# 導入*,實際上就是把列表中的名字全導入進入了,能夠經過__all__控制*表明的名字有哪些

2.3 兩種方式的比較

impot導入模塊在使用時必須加前綴"模塊."
優勢:確定不會與當前名稱空間中的名字衝突
缺點:加前綴顯得麻煩

from.. . impat.. .導入模塊在使用時不用加前綴
優勢:代碼更精簡
缺點:容易與當前名稱空間混淆

3. 循環導入

極力不推薦在程序中設計有模塊的循環導入,可是若是真的遇到,記住兩種解決方式

m1.py

print("正在導入m1")

from m2 import y

x = 1

m2.py

print("正在導入m2")

from m1 import x

y = 2

run.py

import m1


# 運行代碼,首先會加載m1的名稱空間,而後運行裏面的代碼,當運行到第二句的時候,此時m1的名稱空間中尚未x這個名字,因爲是首次導入會加載m2的名稱空間,運行m2的代碼,因此控制檯會打印正在導入m1,正在導入m2,當運行當m2的第二句代碼時,因爲m1不是首次導入,因此不會再加載名稱空間,也不會執行代碼,可是m2問m1要x這個名字,因爲m1中的x這個名字尚未加載到名稱空間,因此程序報錯

[解決方法]

# 方式1:
# 將m1,和m2中的from m2 import y,from m1 import x,放在文件的最後一行,確保導入的時候,名字已經徹底加載到名稱空間了

# 方式2:
# 將模塊導入的代碼,放在函數體中,這樣保證在運行代碼的時候,函數未調用函數體裏面的代碼不執行

4. 搜索模塊路徑的優先級

不管是 import仍是from ...import 在導入模塊時都涉及到查找問題

[優先級]

  1. 內存 (內置模塊,默認會自動加載到內存中)
  2. 硬盤 : 按照sys.path(環境變量)中存放的文件的順序依次查找要導入的模塊
import sys
print(sys.path)

['E:\\project\\python\\s29code\\day21', 'E:\\project\\python\\s29code', 'E:\\developTools\\pycharm\\PyCharm 2020.1\\plugins\\python\\helpers\\pycharm_display', 'E:\\developTools\\Anaconda3\\python36.zip', 'E:\\developTools\\Anaconda3\\DLLs', 'E:\\developTools\\Anaconda3\\lib', 'E:\\developTools\\Anaconda3', 'E:\\developTools\\Anaconda3\\lib\\site-packages', 'E:\\developTools\\Anaconda3\\lib\\site-packages\\win32', 'E:\\developTools\\Anaconda3\\lib\\site-packages\\win32\\lib', 'E:\\developTools\\Anaconda3\\lib\\site-packages\\Pythonwin', 'E:\\developTools\\pycharm\\PyCharm 2020.1\\plugins\\python\\helpers\\pycharm_matplotlib_backend']

# 其中第一個文件夾是當前執行文件所在的文件夾  E:\\project\\python\\s29code\\day21
# 其中第二個文件夾是當前項目的文件夾  E:\\project\\python\\s29code
# 這個值是pycharm自動添加的,因此當他不存在,值裏面的壓縮包就當文件夾看待

[補充]

經過sys.modules查看已將加載到內存的模塊,內容格式 {'模塊名':'內存地址'}

print('foo' in sys.modules)   # 判斷foo模塊是否在當前內存中
print('m1' in sys.modules)    # 判斷foo模塊是否在當前內存中

{'builtins': <module 'builtins' (built-in)>, 'sys': <module 'sys' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (frozen)>, '_imp': <module '_imp' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_thread': <module '_thread' (built-in)>, '_weakref': <module '_weakref' (built-in)>, '_frozen_importlib_external': <module 'importlib._bootstrap_external' (frozen)>, '_io': <module 'io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'nt': <module 'nt' (built-in)>,...}

[python的優化機制]

導入模塊會發生模塊名和對應的內存地址的捆綁關係,可是你經過del 模塊名
是沒法從內存中把那塊空間回收的,由於python導入一個模塊是較耗費內存的
若是你中途從內存中刪除了,再導入就會再開闢內存空間,極大的消耗內存
一樣你在函數內部中導入模塊,函數調用結束後,導入的模塊在內存中也不會回收
只有噹噹前運行文件結束,無其餘文件對模塊中的名字有引用,模塊纔會從內存中消失

5. 模塊規範寫法

咱們在編寫py文件時,須要時刻提醒本身,該文件既是給本身用的,也有可能會被其餘人使用,

於是代碼的可讀性與易維護性顯得十分重要,爲此咱們在編寫一個模塊時最好按照統一的規範去編寫,以下

"The module is used to..."  # 模塊的文檔描述

import sys  # 導入模塊

x = 1  # 定義全局變量,若是非必須,則最好使用局部變量,這樣能夠提升代碼的易維護性,而且能夠節省內存提升性能


class Foo:  # 定義類,並寫好類的註釋
    'Class Foo is used to...'
    pass


def test():  # 定義函數,並寫好函數的註釋
    'Function test is used to…'
    pass


if __name__ == '__main__':  # 主程序
    test()  # 在被當作腳本執行時,執行此處的代碼

6. py文件的用途

6.1 當作模塊導入

# foo.py
print('======m=====')
x = 0
def get():
    print("get ========%s" % x)


def change():
    global x
    x += 1
    return x
# run.py
import foo      # 首次導入模塊,執行代碼
#print(foo)

print(foo.x)    # 0
foo.change()    # 把x指向的內存地址改變了,指向了1
print(foo.x)    # 1

x = 1
y = 2

print('11111')
print('22222')
image-20201211202054880
# 名稱空間的回收順序
首次導入foo模塊,foo代碼會運行,可是代碼運行結束後它的名稱空間沒有被回收,由於裏面的名字被run.py引用了,緊接着run.py中的代碼運行完畢後,它的名稱空間裏面的名字沒有被其餘引用,運行完後,run.py的名稱空間就被回收了,foo的名稱空間也被回收了

# 總結: 模塊沒有被其餘文件引用就被回收了

5.2 當作程序運行

若是一個模塊在開發測試的時候,確定會要運行,可是若是被別人導入的時候,不想執行裏面的函數怎麼辦呢?

# foo.py
print('======m=====')
x = 100
def get():
    print("get ========%s" % x)


def change():
    global x
    x += 1
    return x

if __name__ == '__main__':
    # 看成程序運行的時候作的事情
    get()
    change()
else:
    # 被當作模塊導入的時候作的事情
    pass
# 每個py文件內部都有一個__name__變量,
# 當文件當作程序運行的時候,__name__ == __main__
# 當文件當作模塊導入時,__name__ == 模塊名,即去掉後綴的文件名
相關文章
相關標籤/搜索