人妖在线一区,国产日韩欧美一区二区综合在线,国产啪精品视频网站免费,欧美内射深插日本少妇

新聞動態(tài)

簡單談談Python中的模塊導入

發(fā)布日期:2021-12-24 15:52 | 文章來源:站長之家

本文不討論 Python 的導入機制(底層實現(xiàn)細節(jié)),僅討論模塊與包,以及導入語句相關(guān)的概念。通常,導入模塊都是使用如下語句:

import ...
import ... as ...
from ... import ...
from ... import ... as ...

一般情況下,使用以上語句導入模塊已經(jīng)夠用的。但是在一些特殊場景中,可能還需要其他的導入方式。例如 Python 還提供了 __import__ 內(nèi)建函數(shù)和 importlib 模塊來實現(xiàn)動態(tài)導入。動態(tài)導入的好處是可以延遲模塊的加載,僅在用到模塊時才支持導入動作。

運用 __import__ 函數(shù)和 importlib 模塊固然能夠?qū)崿F(xiàn)模塊的延遲加載,但其不足之處是,在任何需要用到指定模塊的地方都要實現(xiàn)一遍同樣的導入語句,這樣是不便于維護且非常麻煩的。如果能夠在頂層實現(xiàn)惰性導入,則是一個更好的選擇,這也是本文最終要討論的點。

在討論一些高級用法之前,首先需要了解一下模塊與包的概念。

模塊與包

模塊 可以理解為是 Python 可以加載并執(zhí)行的代碼文件,代碼文件不僅可以是 .py 文件,還可以是 .so 等其他類型的文件。Python 只有一個 模塊 對象型態(tài),而且所有模塊都是這個型態(tài)。為了便于組織多個模塊并提供一個模塊層次結(jié)構(gòu)的命名,Python 提供了 包 的概念。

可以簡單的將包看作是一個文件系統(tǒng)的目錄,將模塊看作是目錄中的代碼文件(注意,不能完全地這樣認為,因為包和模塊并非僅來自文件系統(tǒng),還可以來自壓縮文件、網(wǎng)絡(luò)等)。類似于文件系統(tǒng)的目錄結(jié)構(gòu),包被分級組織起來,而且包本身也可以包含子包和常規(guī)模塊。

包其實可以看作是一種特殊的模塊。例如常規(guī)包(下面會介紹常規(guī)包的概念)的目錄中需要包含 __init__.py 文件,當包被導入時,該文件的頂層代碼被隱式執(zhí)行,就如同模塊導入時頂層代碼被執(zhí)行,該文件就像是包的代碼一樣。所以 包是一種特殊的模塊。需要記住的是,所有的包都是模塊,但不是所有的模塊都是包。包中子包和模塊都有 __path__ 屬性,具體地說,任何包含 __path__ 屬性的模塊被認為是包。所有的模塊都有一個名稱,類似于標準屬性訪問語法,子包與他們父包的名字之間用點隔開。

Python 定義了兩種類型的包,即 常規(guī)包 和 命名空間包。常規(guī)包是存在于 Python 3.2 及更早版本中的傳統(tǒng)包。常規(guī)包即包含 __init__.py 文件的目錄。當導入一個常規(guī)包時,__init__.py 文件被隱式執(zhí)行,而且它定義的對象被綁定到包命名空間中的名稱。 __init__.py 文件能包含其他任何模塊能夠包含的相同的 Python 代碼,而且在導入時,Python 將給模塊增加一些額外的屬性。

從 Python 3.3 開始,Python 引入了 命名空間包 的概念。命名空間包是不同文件集的復合,每個文件集給父包貢獻一個子包,所有的包中都不需要包含 __init__.py 文件。文件集可以存于文件系統(tǒng)的不同位置。文件集的查找包含導入過程中 Python 搜索的壓縮文件,網(wǎng)絡(luò)或者其他地方。命名空間包可以但也可以不與文件系統(tǒng)的對象直接對應,它們可以是真實的模塊但沒有具體的表述。更新關(guān)于命名空間包的說明可以參考 PEP 420。

命名空間包的 __path__ 屬性與常規(guī)包不同,其使用自定義的迭代器類型,遍歷所有包含該命令空間包的路徑。如果他們父包的路徑(或者高階包的 sys.path )改變,它將在下次試圖導入時在該包中自動重新搜索包部分。

如有如下目錄結(jié)構(gòu):

.
├── bar-package
│ └── nsp
│ └── bar.py
└── foo-package
└── nsp
└── foo.py

則 nsp 即可以是一個命名空間包,以下是測試代碼(記得用 Python 3.3 及更高版本運行測試):

import sys
sys.path.extend(['foo-package', 'bar-package'])
import nsp
import nsp.bar
import nsp.foo
print(nsp.__path__)
# 輸出:
# _NamespacePath(['foo-package/nsp', 'bar-package/nsp'])

命名空間包具有如下特性:

1、優(yōu)先級最低,在已有版本所有的 import 規(guī)則之后

2、包中不必再包含 __init__.py 文件

3、可以導入并組織目錄分散的代碼

4、依賴于 sys.path 中從左到右的搜索順序

__import__

__import__ 函數(shù)可用于導入模塊,import 語句也會調(diào)用函數(shù)。其定義為:

__import__(name[, globals[, locals[, fromlist[, level]]]])

參數(shù)介紹:

  • name (required): 被加載 module 的名稱
  • globals (optional): 包含全局變量的字典,該選項很少使用,采用默認值 global()
  • locals (optional): 包含局部變量的字典,內(nèi)部標準實現(xiàn)未用到該變量,采用默認值 - local()
  • fromlist (Optional): 被導入的 submodule 名稱
  • level (Optional): 導入路徑選項,Python 2 中默認為 -1,表示同時支持 absolute import 和 relative import。Python 3 中默認為 0,表示僅支持 absolute import。如果大于 0,則表示相對導入的父目錄的級數(shù),即 1 類似于 '.',2 類似于 '..'。

使用示例:

# import spam
spam = __import__('spam')
# import spam.ham
spam = __import__('spam.ham')
# from spam.ham import eggs, sausage as saus
_temp = __import__('spam.ham', fromlist=['eggs', 'sausage'])
eggs = _temp.eggs
saus = _temp.sausage

模塊緩存

在執(zhí)行模塊導入時,Python 的導入系統(tǒng)會首先嘗試從 sys.modules 查找。sys.modules 中是所有已導入模塊的一個緩存,包括中間路徑。即,假如 foo.bar.baz 被導入,那么,sys.modules 將包含進入 foo,foo.bar 和 foo.bar.baz 模塊的緩存。其實一個 dict 類型,每個鍵都有自己的值,對應相應的模塊對象。

導入過程中,首先在 sys.modules 中查找模塊名稱,如果存在,則返回該模塊并結(jié)束導入過程。如果未找到模塊名稱,Python 將繼續(xù)搜索模塊(從 sys.path 中查找并加載)。sys.modules 是可寫的,刪除一個鍵會使指定模塊的緩存實現(xiàn),下次導入時又將重新搜索指定的模塊,這類似于模塊的 reload。

需要注意的是,如果保持模塊對象引用,并使 sys.modules 中緩存失效,然后再重新導入指定的模塊,則這兩個模塊對象是不相同的。而相比之下,importlib.reload() 重新加載模塊時,會使用相同的模塊對象,并通過重新運行模塊代碼簡單地重新初始化模塊內(nèi)容。

imp 與 importlib 模塊

imp 模塊提供了一些 import 語句內(nèi)部實現(xiàn)的接口。例如模塊查找(find_module)、模塊加載(load_module)等等(模塊的導入過程會包含模塊查找、加載、緩存等步驟)??梢杂迷撃K來簡單實現(xiàn)內(nèi)建的 __import__ 函數(shù)功能:

import imp
import sys
def __import__(name, globals=None, locals=None, fromlist=None):
 # 首先從緩存中查找
 try:
  return sys.modules[name]
 except KeyError:
  pass
 # 如果模塊緩存中沒有,則開始從 sys.path 中查找模塊
 fp, pathname, description = imp.find_module(name)
 # 如何找到模塊則將其載入
 try:
  return imp.load_module(name, fp, pathname, description)
 finally:
  if fp:
fp.close()

importlib 模塊在 python 2.7 被創(chuàng)建,并且僅包含一個函數(shù):

importlib.import_module(name, package=None)

這個函數(shù)是對 __import__ 的封裝,以用于更加便捷的動態(tài)導入模塊。例如用其實現(xiàn)相對導入:

import importlib
# 類似于 'from . import b'
b = importlib.import_module('.b', __package__)

從 python 3 開始,內(nèi)建的 reload 函數(shù)被移到了 imp 模塊中。而從 Python 3.4 開始,imp 模塊被否決,不再建議使用,其包含的功能被移到了 importlib 模塊下。即從 Python 3.4 開始,importlib 模塊是之前 imp 模塊和 importlib 模塊的合集。

惰性導入

前邊介紹的大部分內(nèi)容都是為實現(xiàn)惰性導入做鋪墊,其他的小部分內(nèi)容僅是延伸而已(就是隨便多介紹了點內(nèi)容)。惰性導入即延遲模塊導入,在真正用到模塊時才執(zhí)行模塊的導入動作,如果模塊不被使用則導入動作永遠不會發(fā)生。

惰性導入的需求還是很常見的。一般推薦模塊僅在頂層導入,而有時候在頂層導入模塊并非最好的選擇。比如,一個模塊僅在一個函數(shù)或者類方法中用到時,則可使用局部導入(在局部作用域中執(zhí)行導入),使得僅在函數(shù)或方法被執(zhí)行時才導入模塊,這樣可以避免在頂層名字空間中引入模塊變量。再比如,在我工作所負責的項目中,需要用到 pandas 包,而 pandas 包導入了會占用一些內(nèi)存(不是很多,但也不算少,幾十兆的樣子),所以當不會用到 pandas 包時,我們希望他不被導入。我們自己實現(xiàn)的一些包在載入時會很耗時(因為要讀取配置等等,在導入時就會耗時幾秒到十幾秒的樣子),所以也極其需要惰性導入的特性。

下面是惰性導入的簡單實現(xiàn),可供參考:

import sys
from types import ModuleType

class LazyModuleType(ModuleType):
 @property
 def _mod(self):
  name = super(LazyModuleType, self).__getattribute__("__name__")
  if name not in sys.modules:
__import__(name)
  return sys.modules[name]
 def __getattribute__(self, name):
  if name == "_mod":
return super(LazyModuleType, self).__getattribute__(name)
  try:
return self._mod.__getattribute__(name)
  except AttributeError:
return super(LazyModuleType, self).__getattribute__(name)
 def __setattr__(self, name, value):
  self._mod.__setattr__(name, value)

def lazy_import(name, package=None):
 if name.startswith('.'):
  if not package:
raise TypeError("relative imports require the 'package' argument")
  level = 0
  for character in name:
if character != '.':
 break
level += 1
  if not hasattr(package, 'rindex'):
raise ValueError("'package' not set to a string")
  dot = len(package)
  for _ in range(level, 1, -1):
try:
 dot = package.rindex('.', 0, dot)
except ValueError:
 raise ValueError("attempted relative import beyond top-level "
  "package")
  name = "{}.{}".format(package[:dot], name[level:])
 return LazyModuleType(name)

總結(jié)

到此這篇關(guān)于Python中模塊導入的文章就介紹到這了,更多相關(guān)Python模塊導入內(nèi)容請搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!

參考資料

  • https://docs.python.org/3/reference/import.html
  • https://github.com/nipy/nitime
  • https://github.com/mnmelo/lazy_import

版權(quán)聲明:本站文章來源標注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務器上建立鏡像,否則將依法追究法律責任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學習參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。

相關(guān)文章

實時開通

自選配置、實時開通

免備案

全球線路精選!

全天候客戶服務

7x24全年不間斷在線

專屬顧問服務

1對1客戶咨詢顧問

在線
客服

在線客服:7*24小時在線

客服
熱線

400-630-3752
7*24小時客服服務熱線

關(guān)注
微信

關(guān)注官方微信
頂部