1. 程式人生 > >【轉】Python標準模組--importlib

【轉】Python標準模組--importlib

【轉】Python標準模組--importlib

作者:zhbzz2007 出處:http://www.cnblogs.com/zhbzz2007 歡迎轉載,也請保留這段宣告。謝謝!

1 模組簡介

Python提供了importlib包作為標準庫的一部分。目的就是提供Python中import語句的實現(以及__import__函式)。另外,importlib允許程式設計師建立他們自定義的物件,可用於引入過程(也稱為importer)。

什麼是imp?
另外有一個叫做imp的模組,它提供給Python import語句機制的介面。這個模組在Python 3.4中被否決,目的就是為了只使用importlib。

這個模組有些複雜,因此我們在這篇博文中主要討論以下幾個主題:

  • 動態引入
  • 檢查模組是否可以被引入
  • 引入原始檔自身
  • 第三方模組 import_from_github_com

2 模組使用

2.1 動態引入

importlib模組支援傳入字串來引入一個模組。我們建立兩個簡單的模組來驗證這個功能。我們將會給予兩個模組相同的介面,讓它們列印名字以便我們能夠區分它們。建立兩個模組,分別為foo.py和bar.py,程式碼如下所示,

def main():
    print(__name__)

現在我們使用importlib來引入它們。讓我們看看這段程式碼如何去做的。確保你已經把這段程式碼放在與上面建立的兩個模組相同的目錄下。

#importer.py
import importlib

def dynamic_import(module):
    return importlib.import_module(module)

if __name__ == "__main__":
    module = dynamic_import('foo')
    module.main()

    module_two = dynamic_import('bar')
    module_two()

在這段程式碼中,我們手動引入importlib模組,並建立一個簡單的函式dynamic_import。這個函式所做的就是呼叫importlib模組中的import_module函式,入參就是我們傳入的字串,然後返回呼叫結果。在程式碼段的下面,我們呼叫每個模組的main方法,將會打印出每個模組的名稱。

在你的程式碼中,你可能不會大量這樣做。當你只有一個字串時,如果你想引入這個模組,importlib就允許你可以這麼做。

2.2 模組引入檢查

Python有一個編碼規範就是EAPP:Easier to ask for forgiveness than permision。意思就是經常假設一些事情是存在的(例如,key在詞典中),如果出錯了,那麼就捕獲異常。你可以看 Python標準模組--import 文章中我們嘗試引入模組,當它不存在時,我們就會捕獲到ImportError。如果我們想檢查並觀察一個模組是否可以引入而不是僅僅是猜測,該如何去做?你可以使用importlib。程式碼如下:

#coding:utf-8

import importlib.util
import importlib

def check_module(module_name):
    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):
    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,我們的程式碼將會打印出這個模組沒有找到。如果找到了,我們就會返回模組的說明。

我們可以獲取到模組的說明,然後使用它來真正的引入模組。或者你可以將字串傳入到import_module函式中,正如我們在2.1節中所學習到的一樣。但是我們已經學習到如何使用模組的說明。讓我們看一下上述程式碼中的import_module_from_spec函式。它接受由check_module函式返回的模組說明。我們將其傳入到module_from_spec函式,它將會返回引入的模組。Python的官方文件推薦,在引入模組後執行它,所以我們下一步做的就是呼叫exec_module函式。最後我們返回這個模組,並且執行Python的dir函式來確認這個我們就是我們所期望的。

2.3 從原始檔中引入

在這一節中,我想說明importlib的子模組util還有另外一個技巧。你可以使用util通過模組名和檔案路徑來引入一個模組。示例如下所示,

#coding:utf-8

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函式。在這個函式中,我們首先獲取到模組的實際路徑和名稱。然後我們將這些資訊傳入到util的spec_from_file_location函式中,這個將會返回模組的說明。一旦我們獲取到模組的說明,我們就可以使用與2.2節相同的importlib機制來實際引入模組。

現在讓我們來看一個精巧的第三方庫,Python的__import__()函式直接引入github中的包。

2.4 import_from_github_com

這個精巧的包叫做import_from_github_com,它可以用於發現和下載github上的包。為了安裝他,你需要做的就是按照如下命令使用pip,

pip install import_from_github_com

這個包使用了PEP 302中新的引入鉤子,允許你可以從github上引入包。這個包實際做的就是安裝這個包並將它新增到本地。你需要Python 3.2或者更高的版本,git和pip才能使用這個包。

一旦這些已經安裝,你可以在Python shell中輸入如下命令,

>>> from github_com.zzzeek import sqlalchemy
Collecting git+https://github.com/zzzeek/sqlalchemy
Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build
Installing collected packages: SQLAlchemy
Running setup.py install for SQLAlchemy ... done
Successfully installed SQLAlchemy-1.1.0b1.dev0
>>> locals()
{'__builtins__': <module 'builtins' (built-in)>, '__spec__': None,
'__package__': None, '__doc__': None, '__name__': '__main__',
'sqlalchemy': <module 'sqlalchemy' from '/usr/local/lib/python3.5/site-packages/\
sqlalchemy/__init__.py'>,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>}

你如果看了import_from_github_com的原始碼,你將會注意到它並沒有使用importlib。實際上,它使用了pip來安裝那些沒有安裝的包,然後使用Python的__import__()函式來引入新安裝的模組。這段程式碼非常值得學習。

2.5 總結

到這裡,你已經瞭解到在你的程式碼中如何使用importlib和引入鉤子。當然還有很多超出本文所覆蓋的知識,如果你需要寫一個自定義的引入器或者下載器,你需要花費很多時間來閱讀官方文件和原始碼。

3 Reference

Python 201