Python實(shí)現(xiàn)創(chuàng)建模塊的方法詳解
楔子
導(dǎo)入一個(gè)模塊,我們一般都會(huì)使用 import 關(guān)鍵字,但有些場(chǎng)景下 import 難以滿(mǎn)足我們的需要。所以除了 import 之外還有很多其它導(dǎo)入模塊的方式,下面就來(lái)介紹一下。
__import__
這是一個(gè)內(nèi)置函數(shù),解釋器在 import 的時(shí)候,實(shí)際上就執(zhí)行了這個(gè)函數(shù)。
#?import?os?等價(jià)于如下方式 os?=?__import__("os") print(os)??#?<module?'os'?from?'C:\\python38\\lib\\os.py'> #?但是這種方式不能多級(jí)導(dǎo)入 path?=?__import__("os.path") print(path)??#?<module?'os'?from?'C:\\python38\\lib\\os.py'> #?可以看到,導(dǎo)入的仍是?os,而不是 os.path #?如果想導(dǎo)入子模塊,需要一個(gè)參數(shù)?fromlist #?我們給它傳一個(gè)非空列表即可 path?=?__import__("os.path",?fromlist=[""]) print(path)??#?<module?'ntpath'?from?'C:\\python38\\lib\\ntpath.py'>
但是官方不建議使用這個(gè)函數(shù),因?yàn)樗菍?zhuān)門(mén)給解釋器用的,我們可以使用一個(gè)模塊。
import?importlib os?=?importlib.import_module("os") print(os)??#?<module?'os'?from?'C:\\python38\\lib\\os.py'> #?可以多級(jí)導(dǎo)入 path?=?importlib.import_module("os.path") print(path)??#?<module?'ntpath'?from?'C:\\python38\\lib\\ntpath.py'>
所以當(dāng)導(dǎo)入的模塊名以字符串的形式存在時(shí),或者模塊名不符合規(guī)范時(shí),就可以使用這種方式。
importlib.machinery
importlib.machinery 里面提供了三種 Loader,可以讓我們以打開(kāi)文件的方式導(dǎo)入一個(gè)模塊。
from?importlib.machinery?import?( ????SourceFileLoader,??#?導(dǎo)入源文件 ????SourcelessFileLoader,??#?導(dǎo)入 pyc?文件 ????ExtensionFileLoader??#?導(dǎo)入擴(kuò)展文件 ) #?參數(shù)一:給模塊起個(gè)名字 #?參數(shù)二:文件路徑 os?=?SourceFileLoader( ????"我是?os?模塊", ????r"C:\python38\lib\os.py" ).load_module() print(os) """ <module?'我是?os?模塊'?from?'C:\\python38\\lib\\os.py'> """ print(os.path.join("video",?"overwatch",?"hanzo.mp4")) """ video\overwatch\hanzo.mp4 """ #?我們看到結(jié)果一切正常,但有一點(diǎn)需要注意 #?如果是導(dǎo)入包的話(huà),那么要導(dǎo)入包里面的?__init__.py?文件 pd?=?SourceFileLoader( ????"我是?pandas?模塊", ????r"C:\python38\lib\site-packages\pandas\__init__.py" ).load_module() print(pd.DataFrame({"a":?[1,?2,?3],?"b":?[4,?5,?6]})) """ ???a??b 0??1??4 1??2??5 2??3??6 """ #?如果只寫(xiě)到?pandas,那么會(huì)拋出?PermissionError #?因?yàn)槲覀儾荒馨涯夸洰?dāng)成文件來(lái)讀取 #?至于?import?一個(gè)包,本質(zhì)上也是加載包內(nèi)部的?__init__.py? #?但這里需要我們顯式地加上?__init__.py
同理加載 pyc 和 pyd 也是類(lèi)似的,但需要注意的是,加載普通文件和 pyc 文件時(shí),我們可以隨便起名字,也就是第一個(gè)參數(shù)任意。但對(duì)于 pyd 文件,第一個(gè)參數(shù)必須和 pyd 文件的名字保持一致。
通過(guò) module 類(lèi)創(chuàng)建模塊
Python 一切皆對(duì)象,模塊自然也不例外。既然是對(duì)象,那么必然就會(huì)有相應(yīng)的類(lèi)來(lái)實(shí)例化它。
import?os import?hashlib import?numpy #?os.__class__?等價(jià)于?type(os) print(os.__class__)??#?<class?'module'> print(hashlib.__class__)??#?<class?'module'> print(numpy.__class__)??#?<class?'module'>
在 Python 里面,我們一般會(huì)把單獨(dú)的可導(dǎo)入文件稱(chēng)之為模塊,把包含多個(gè)模塊的目錄稱(chēng)之為包。通過(guò)模塊和包,我們可以對(duì)項(xiàng)目進(jìn)行功能上的劃分,分門(mén)別類(lèi)地進(jìn)行組織。
但不管是模塊、還是包,它們都是 module 這個(gè)類(lèi)的實(shí)例對(duì)象,打印結(jié)果也能說(shuō)明這一點(diǎn)。所以從解釋器的角度來(lái)看的話(huà),模塊和包區(qū)分的并沒(méi)有那么明顯,直接把包看做是包內(nèi)部的 __init__.py 即可。
既然模塊的類(lèi)型是 class module,那么我們是不是也可以通過(guò)調(diào)用類(lèi)型對(duì)象的方式創(chuàng)建呢?顯然是可以的,但是 module 這個(gè)類(lèi)解釋器沒(méi)有暴露給我們,直接用的話(huà)會(huì)提示變量 module 未定義。所以只能先隨便導(dǎo)入一個(gè)模塊,然后通過(guò) type 函數(shù)或者 __class__ 屬性獲取。
#?不過(guò)?types?模塊內(nèi)部已經(jīng)幫我們做好了 #?ModuleType?=?type(sys) from?types?import?ModuleType print(ModuleType)??#?<class?'module'> #?類(lèi)對(duì)象有了,下面就可以創(chuàng)建了 #?module?類(lèi)接收兩個(gè)參數(shù) #?參數(shù)一:模塊的名字,必須傳遞 #?參數(shù)二:模塊的 doc,不傳默認(rèn)為 None satori?=?ModuleType("古明地覺(jué)",?"模塊的名字是一個(gè)女孩,她來(lái)自地靈殿") print(satori)??#?<module?'古明地覺(jué)'> print(satori.__doc__)??#?模塊的名字是一個(gè)女孩,她來(lái)自地靈殿 #?但此時(shí)模塊里面是沒(méi)啥東西的,我們加一些屬性吧 #?操作模塊本質(zhì)上是在操作它的屬性字典 code?=?""" age?=?16 def?foo(): ????return?"^_^" """ #?執(zhí)行?code,結(jié)果會(huì)體現(xiàn)在?satori?的屬性字典中 exec(code,?satori.__dict__) print(satori.age)??#?16 print(satori.foo())??#?^_^
需要注意的是里面 exec 函數(shù),它會(huì)把字符串當(dāng)成代碼來(lái)執(zhí)行,所以這就要求字符串的來(lái)源必須是可靠的,我們能夠確保不會(huì)出現(xiàn)惡意內(nèi)容。而如果是用戶(hù)傳遞的字符串,那么絕不能用 exec 來(lái)執(zhí)行,當(dāng)然 eval 也是同理。
然后是 exec 的第二個(gè)參數(shù),表示執(zhí)行時(shí)的名字空間,默認(rèn)是全局名字空間。所以當(dāng)不指定第二個(gè)參數(shù)時(shí),exec(code) 相當(dāng)于創(chuàng)建了兩個(gè)全局變量:age 和 foo。
code?=?""" age?=?16 def?foo(): ????return?"^_^" """ exec(code) print(age)??#?16 print(foo())??#?^_^
但是我們?cè)趫?zhí)行的時(shí)候,將它換成了 satori.__dict__,所以結(jié)果相當(dāng)于給模塊添加了兩個(gè)變量,或者說(shuō)屬性。
將一個(gè)類(lèi)的實(shí)例變成一個(gè)模塊
如果想將一個(gè)類(lèi)的實(shí)例變成模塊,那么這個(gè)類(lèi)應(yīng)該繼承 ModuleType。
import?sys from?types?import?ModuleType class?A(ModuleType): ????def?__init__(self,?module_name): ????????super().__init__(module_name) ????def?__getattr__(self,?item): ????????return?f"不存在的屬性:?{item}" ????def?__setattr__(self,?key,?value): ????????self.__dict__[key]?=?value ????def?__str__(self): ????????return?f"<module?'{self.__name__}'?from?'我來(lái)自于虛無(wú)'>" a?=?A("我是?A") print(a)??#?<module?'我是?A'?from?'我來(lái)自于虛無(wú)'> print(a.__name__)??#?我是?A print(a.xx)??#?不存在的屬性:?xx a.xx?=?"xx" print(a.xx)??#?xx #?加入到?sys.modules?中 sys.modules["嘿嘿"]?=?a import?嘿嘿 print(嘿嘿.xx)??#?xx print(嘿嘿.yy)??#?不存在的屬性:?yy
是不是很好玩呢?
小結(jié)
以上就是加載模塊的幾種方式,主要用途如下:
- 導(dǎo)入一個(gè)在 sys.path 中的模塊,并且模塊名已知,那么直接使用 import 關(guān)鍵字即可;
- 導(dǎo)入一個(gè)在 sys.path 中的模塊,但模塊名是運(yùn)行時(shí)的一個(gè)字符串,那么使用 importlib 模塊的 import_module 函數(shù);
- 導(dǎo)入一個(gè)不在 sys.path 中的模塊,使用 importlib.machinery 的各種 Loader,只要把模塊的路徑傳進(jìn)去即可。當(dāng)然啦,位于 sys.path 中的模塊也可以使用該方法,但顯然此時(shí)使用前兩種更為方便;
- 直接創(chuàng)建一個(gè)模塊,通過(guò)繼承 module 類(lèi)來(lái)實(shí)現(xiàn),并且還可以加入到 sys.modules 中。Python 有一個(gè)第三方模塊叫 sh,顧名思義是用來(lái)執(zhí)行 Linux Shell 命令的,它內(nèi)部就使用了繼承 module 類(lèi)來(lái)創(chuàng)建模塊的這種方式。但是要知道 module 這個(gè)類(lèi)解釋器沒(méi)有暴露給我們,我們需要通過(guò) type(模塊) 或者 模塊.__class__ 的方式獲??;
到此這篇關(guān)于Python實(shí)現(xiàn)創(chuàng)建模塊的方法詳解的文章就介紹到這了,更多相關(guān)Python創(chuàng)建模塊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
tf.truncated_normal與tf.random_normal的詳細(xì)用法
本篇文章主要介紹了tf.truncated_normal與tf.random_normal的詳細(xì)用法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Python通過(guò)Django實(shí)現(xiàn)用戶(hù)注冊(cè)和郵箱驗(yàn)證功能代碼
這篇文章主要介紹了Python通過(guò)Django實(shí)現(xiàn)用戶(hù)注冊(cè)和郵箱驗(yàn)證功能代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12Python RobotFramework的安裝過(guò)程及應(yīng)用實(shí)戰(zhàn)教程
這篇文章主要介紹了RobotFramework的安裝過(guò)程及應(yīng)用實(shí)戰(zhàn)教程,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08Python函數(shù)中apply、map、applymap的區(qū)別
這篇文章主要介紹了 Python函數(shù)中apply、map、applymap的區(qū)別 ,文章圍繞 Python函數(shù)中apply、map、applymap的相關(guān)資料展開(kāi)詳細(xì)內(nèi)容,需要的朋友可以參考一下2021-11-11用python獲取txt文件中關(guān)鍵字的數(shù)量
這篇文章主要介紹了如何用python獲取txt文件中關(guān)鍵字的數(shù)量,幫助大家更好的理解和使用python,感興趣的朋友可以了解下2020-12-12Python實(shí)現(xiàn)合并PDF文件的三種方式
在處理多個(gè) PDF 文檔時(shí),頻繁地打開(kāi)關(guān)閉文件會(huì)嚴(yán)重影響效率,因此我們可以先將這些PDF文件合并起來(lái)再操作,本文將分享3種使用 Python 合并 PDF 文件的實(shí)現(xiàn)方法,希望對(duì)大家有所幫助2023-11-11