Python將importlib作為標准庫提供。它旨在提供Pythonimport語法和(__import__()函數)的實現。另外,importlib提供了開發者可以創建自己的對象(即importer)來處理導入過程。
那么imp呢?還有一個imp模塊提供了import語句接口,不過這個模塊在Python3.4已經deprecated了。建議使用importlib來處理。
這個模塊比較復雜,文中我們主要探討如下主題:
- 動態導入
- 檢查模塊是否可以導入
- 從源文件導入
我們先從動態導入開始。
動態導入
importlib模塊支持傳遞字符串來導入模塊。我們先來創建一些簡單模塊一遍演示。我們在模塊里提供了相同接口,通過打印它們自身名字來區分。我們分別創建了foo.py和bar.py,代碼如下:
def main():
print(__name__)
現在我們盡需要使用importlib導入它們。我們來看看代碼是如何實現的,確保該代碼在剛才創建的兩個文件的相同目錄下。
#importer
import importlib def dynamic_import(module): return importlib.import_module(module) if __name__ == "__main__": module = dynamic_import('foo') module.main() module2 = dynamic_import('bar') module2.main()
這里我們導入importlib模塊,並創建了一個非常簡單的函數dynamic_import。這個函數直接就調用了importlib的import_module方法,並將要導入的模塊字符串傳遞作為參數,最后返回其結果。然后在主入口中我們分別調用了各自的main方法,將打印出各自的name.
$ python3 importer.py
foo
bar
也許你很少會代碼這么做,不過在你需要試用字符串作為導入路徑的話,那么importlib就有用途了。
模塊導入檢查
Python有個眾所周知的代碼風格EAFP: Easier to ask forgiveness than permission.它所代表的意思就是總是先確保事物存在(例如字典中的鍵)以及在犯錯時捕獲。如果我們在導入前想檢查是否這個模塊存在而不是靠猜。 使用mportlib就能實現。
import importlib.util def check_module(module_name): """ Checks if module can be imported without actually importing it """ module_spec = importlib.util.find_spec(module_name) if module_spec is None: print("Module: {} not found".format(module_name)) return None else: print("Module: {} can be imported".format(module_name)) return module_spec def import_module_from_spec(module_spec): """ Import the module via the passed in module specification Returns the newly imported module """ module = importlib.util.module_from_spec(module_spec) module_spec.loader.exec_module(module) return module if __name__ == '__main__': module_spec = check_module('fake_module') module_spec = check_module('collections') if module_spec: module = import_module_from_spec(module_spec) print(dir(module))
這里我導入了importlib的子模塊util。check_module里面調用find_spec方法, 傳遞該模塊字符串作為參數。當我們分別傳入了一個不存在和存在的Python模塊。你可以看到當你傳入不存在的模塊時,find_spec函數將返回 None,在我們代碼里就會打印提示。如果存在我們將返回模塊的specification。
我們可以通過該模塊的specification來實際導入該模塊。或者你直接將字符串作為參數調用import_module函數。不過我這里也學習如何試用模塊specification方式導入。看看import_module_from_spec函數。它接受check_module提供的模塊specification作為參數。然后我們將它傳遞給了module_from_spec函數,它將返回導入模塊。Python文檔推薦導入后然后執行模塊,所以接下來我們試用exec_module函數執行。最后我們使用dir來確保得到預期模塊。
從源代碼導入
importlib的子模塊有個很好用的技巧我想提提。你可以使用util通過模塊的名字和路徑來導入模塊。
import importlib.util def import_source(module_name): module_file_path = module_name.__file__ module_name = module_name.__name__ module_spec = importlib.util.spec_from_file_location( module_name, module_file_path ) module = importlib.util.module_from_spec(module_spec) module_spec.loader.exec_module(module) print(dir((module))) msg = 'The {module_name} module has the following methods {methods}' print(msg.format(module_name=module_name, methods=dir(module))) if __name__ == "__main__": import logging import_source(logging)
在上面的代碼中,我們實際導入logging模塊,並將模塊傳遞給了import_source函數。這樣我們就可以通過導入的模塊獲取到實際的路 徑和名字。然后我們將信息傳遞給sec_from_file_location函數,它將返回模塊的specification。也了這個我們就可以在前 面那樣直接通過importlib導入了。
總結
目前,你知道如何在代碼中使用importlib和import鈎子。這個模塊內容非常多,如果你想自定義importer或者loader,那么你可以通過官方文檔或者源代碼了解更多。