模块与包
一、模塊
常見的場景:一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的后綴,但其實import加載的模塊分為四個通用類別:
- 使用python編寫的代碼(.py文件)
- 已被編譯為共享或DLL的C或C++擴展
- 包好一組模塊的包
- 使用C編寫并鏈接到python解釋器的內置函數
如果你退出Python解釋器然后再進入,那么你之前定義的函數或變量都將丟失,因此我們通常將程序寫到文件中以便永久保存下來,需要時就通過python 文件.py ,此時執行的文件就叫做script。當文件腳本很多時,我們將程序分成一個個文件,這樣程序的結構就更清晰,方便管理,這時我們不僅僅可以把這些文件當成腳本文件,還可以把他們當作模塊來導入到其他模塊,實現功能的重用
使用模塊: 我們可以從sys.modules中找到當前已經加載的模塊
-
模塊的導入相當于執行了整個文件
-
一個模塊不能被多次導入,一旦導入原文件中的修改也不會生效,且各個模塊的變量于本模塊不沖突
?x #測試一:money與my_module.money不沖突 import my_module ?# 默認有以下屬性 money=10 print(my_module.money) #屬性和方法都可以my_moudle.方法() my_moudle.read() ? ''' 執行結果: from the my_module.py 1000 ''' ? import my_moudle ?# 次文件中有print('123') import my_moudle # 打印一個 123?
-
一個模塊不會被多次執行,只執行一遍,再兩個模塊相互調用時要分清。:第一次導入后就將模塊名加載到內存了,后續的import語句僅是對已經加載大內存中的模塊對象增加了一次引用,不會重新執行模塊內的語句。
-
導入一個模塊的時候命名空間的變化:
- 創建了一個要導入模塊的命名空間
- 創建一個變量指向這個命名空間,其實相當于加載文件類(個人理解)
- 執行這個文件,注意這個關系是單向引用,也就是說,如果不是函數傳參的形式,是不能把本文件的變量a傳給模塊中的變量a
?
-
模塊雖然一行可以導入多個,但是不推薦這樣使用。如:import time,os,random,my_module
-
as語法的使用:
xxxxxxxxxx #1、time這個名字就失效的,只剩下t了 import time as t t.time() ? #2、用來做兼容,當要根據判斷取模塊時,我們可以先判斷,再以同樣的as名字 mode = 'pickle' ?#不確定是 json還是 pickle if mode == 'pickle': ? ?import pickle as mode else: ? ?import json as mode def dump(): ? ?mode.dump(obj,f) def load(): ? ?mode.load(f) ? #其他用法: #1 from my_module import read1 as read #2 from my_module import (read1, ? ? ? ? ? ? ? ? ? ? ? read2, ? ? ? ? ? ? ? ? ? ? ? money) ?
-
from …… import ……from 語句相當于import,也會創建新的名稱空間,但是將my_module中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就可以了
xxxxxxxxxx #1測試一:導入的函數read1,執行時仍然回到my_module.py中尋找全局變量money from my_module import read1 money=1000 read1() ''' 執行結果: from the my_module.py spam->read1->money 1000 ''' ? #測試二:導入的函數read2,執行時需要調用read1(),仍然回到my_module.py中找read1() from my_module import read2 def read1(): ? ?print('==========') read2() ''' 執行結果: from the my_module.py my_module->read2 calling read1 my_module->read1->money 1000 ''' #測試三:導入的函數read1,被當前位置定義的read1覆蓋掉了 ? from my_module import read1 def read1(): ? ?print('==========') read1() ''' 執行結果: from the my_module.py ========== ''' ? #from my_module import * 把my_module中所有的不是以下劃線(_)開頭的名字都導入到當前位置,大部分情況下我們的python程序不應該使用這種導入方式,因為*你不知道你導入什么名字,很有可能會覆蓋掉你之前已經定義的名字。而且可讀性極其的差,在交互式環境中導入時沒有問題 ? #在my_module.py中新增一行: __all__=['money','read1'] #這樣在另外一個文件中用from my_module import *就這能導入列表中規定的兩個名字 ? ? ? ? ? ? ? ? ? #__all__=[] 和 * 配合使用 ? #補充: #如果my_module.py中的名字前加_,即_money,則from my_module import *,則_money不能被導入?
二、模塊導入的三大問題
-
模塊的搜索路徑:
-
python解釋器在啟動時會自動加載一些模塊,可以使用sys.modules查看,在第一次導入某個模塊時(比如my_module),會先檢查該模塊是否已經被加載到內存中(當前執行文件的名稱空間對應的內存),如果有則直接引用.如果沒有,解釋器則會查找同名的內建模塊,如果還沒有找到就從sys.path給出的目錄列表中依次尋找my_module.py文件。
xxxxxxxxxx #1、所以總結模塊的查找順序是:內存中已經加載的模塊 -> 內置模塊 -> ys.path路徑中包含的模塊 #2、需要特別注意的是:我們自定義的模塊名不應該與系統內置模塊重名 #3、搜索時按照sys.path中從左到右的順序查找,位于前的優先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會把.zip歸檔文件當成一個目錄去處理。在pycharm中會自動為我們加載本地路徑 import sys sys.path.append('/a/b/c/d') sys.path.insert(0,'/x/y/z') #排在前的目錄,優先被搜索
-
-
模塊能不能被循環導入:
?
- xxxxxxxxxx #能不能 在 a.py import b ? ? ? #在 b.py import a # 以主程序為主,加載順序從上至下,可引用
?
-
把模塊當做腳本執行:
-
當做腳本運行:__name__ 等于'__main__' :__name__ 等于模塊名
xxxxxxxxxx def fib(n): ? ? ?a, b = 0, 1 ? ?while b < n: ? ? ? ?print(b, end=' ') ? ? ? ?a, b = b, a+b ? ?print() ? if __name__ == "__main__": ?#只有在當前文件下執行,如果是模塊調用則不會執行 ? ?print(__name__) ? ?num = input('num :') ? ?fib(int(num))?
-
三、包
-
包是一種通過使用'模塊名'來組織python模塊名稱空間的方式。
-
無論是import形式還是from...import形式,凡是在導入語句中(而不是在使用時)遇到帶點的,都要第一時間提高警覺:這是關于包才有的導入語法
-
包是目錄級的(文件夾級),文件夾是用來組成py文件(包的本質就是一個包含__init__.py文件的目錄
-
import導入文件時,產生名稱空間中的名字來源于文件,import 包,產生的名稱空間的名字同樣來源于文件,即包下的__init__.py,導入包本質就是在導入該文件
-
在python3中,即使包下沒有__init__.py文件,import 包仍然不會報錯,而在python2中,包下一定要有該文件,否則import 包報錯
-
創建包的目的不是為了運行,而是被導入使用,記住,包只是模塊的一種形式而已,包即模塊
-
import 包:相當于執行了這個包下的init文件
-
具體使用:
xxxxxxxxxx import glance.api.policy as policy ?#假設glance是包,api是文件,policy是py文件 用別名 policy.get() #也可以:import glance.api.policy.get() 但是太長了,每個都要寫 # 根據包的導入要精確到模塊名,不能精確到具體的函數或者變量,然后使用glance.api.policy或者重命名的方式,來使用這個模塊中的所有名字 ? #from……import 在包中的用法 from glance.api import policy ?#import后面不能有. 且至少要精確到模塊 policy.get() ? #or from glance.api.policy import get get() ? #使用from……import import后面至少是精確到模塊的,import后面不能有。from后面可以有.,但是.的左邊永遠是包名 ? ? ? #import 直接導入包時,再導入時執行了包中的__init__.py文件(類似于實例化),我們可以在__init__.py中寫相應的路由,假如我想直接import 一個包,就需要配置__init__.py文件: ?# 1、現在glance(包)中的__init__.py,需要配置: ? ? -
__init__.py文件:不管是哪種方式,只要是第一次導入包或者是包的任何其他部分,都會依次執行包下的init.py文件(我們可以在每個包的文件內都打印一行內容來驗證一下),這個文件可以為空,但是也可以存放一些初始化包的代碼
?
-
首先要想使用import glance 里面的如api下的policy中的方法,先配置glance文件下的__init__.py
xxxxxxxxxx # /glance/__init__.py from glance import api from glance import cmd from glance import db ?# 此時在包外一個文件import glance,可以調用api,cmd,db,但是還不能導入其中的具體方法 ? #test/my_moudle import glance print(glance) ?# <moudle 'glance' from 'D:\\'> print(glance.api) ? #<moudle 'glance.api' from 'D:……> print(glance.api.policy) ?#不行 如何解決,因為沒有在api文件的init中配置路徑 ? #此時在api/__init__.py中輸入 from api import policy ?# 之后再運行還是報錯,問題出在from api這里,找不到api,這時候可以通過sys.path 查看路徑,顯示只能查看能找到glance,也就是說能執行glance中的__init__.py文件,但是不能執行api中的init文件,所以這里有兩種方法解決這個問題: ? #方案一: 在api文件中的__init.py文件中添加路徑 import sys sys.path.append(r'D:\……\glance') #將glace文件加載到系統路徑中,于是會查找其中的各個文件 from api import policy ?# 導入glance后,api文件就會被找到,一層一層的 from api import versions #此時api文件下的文件都可以使用了,但是如果要導入cmd文件下的文件,就還需要在它文件下的init文件做同樣的處理。麻煩 ? ? #方案二:絕對路徑,不需要添加路經,使用絕對導入、 #/glance/api/__init__.py from glance.api import policy ?#效果相同,不需要讓api的上一級成為環境變量中 from glance.api import versions ? ? #總結: 在包中的__init__.py文件中的就是在import時要執行的文件,各級子文件的__init__.py文件最好使用絕對導入,這樣,想導入的時候才能找到?
-
五、絕對導入和相對導入
-
絕對導入:以glance作為起始
相對導入:用.或者..的方式最為起始(只能在一個包中使用,不能用于不同目錄內)
xxxxxxxxxx #在glance/api/version.py ? #絕對導入 from glance.cmd import manage manage.main() ? #相對導入 from ..cmd import manage manage.main() ? #可以用import導入內置或者第三方模塊(已經在sys.path中),但是要絕對避免使用import來導入自定義包的子模塊(沒有在sys.path中),應該使用from... import ...的絕對或者相對導入,且包的相對導入只能用from的形式。 -
單獨導入包:單獨導入包名稱時不會導入包中所有包含的所有子模塊
x #在與glance同級的test.py中 import glance glance.cmd.manage.main() ? ''' 執行結果: AttributeError: module 'glance' has no attribute 'cmd' ? ''' ? #解決辦法: 規劃路經,使用__init__.py,__all__是用于控制from...import * #glance/__init__.py from . import cmd ? #glance/cmd/__init__.py from . import manage ? #執行 #在于glance同級的test.py中 import glance glance.cmd.manage.main()
?
六、軟件開發規范
七、補充:
x # 同一大文件下(項目目錄),如何導入各個小文件中的py文件的導入: __file__:當前文件路徑 #項目開始的文件中導入 import os start_path = __file__ bin_path = os.path.dirname(start_path) ?# 翻一層 project_path = os.path.dirname(bin_path) ?# 再翻 sys.path.append(project_path) ?# 添加路徑,可以遷移文件 ? #規定 Base_path = os.path.dirname(os.path.dirname(__file__)) sys.path.append(project_path) ? #注意的是:在每個模塊的開頭,需要導入時,都需要使用from 父目錄 import ……,也就是說現在項目的所有小目錄能找到,所以,我們的子目錄都是要記錄這些小目錄開始 from。 ??
轉載于:https://www.cnblogs.com/double-W/p/10658835.html
總結
- 上一篇: 第一章:SpringBoot入门
- 下一篇: SpringCloud之Hystrix