Python部落組織翻譯, 歡迎轉發, 禁止轉載
注意:這篇博文中的代碼, 現在已經是PyPI上modutil模組的一部分了。
Python3.7在模組上也添加了__getattr__和__dir__兩個方法。 這個新特性讓我們能夠實現一些有趣的事情。 例如, 通過定義__dir__方法, 你可以要求dir(模組)只顯示__all__中定義的內容了。
但對於我來說, 我更關心__getattr__方法, 以及我如何能利用這個特性實現懶載入。 在本文的開始, 我希望先告訴大家, 大多數人的代碼是不需要懶載入的。 只有當懶載入帶來的好處很有用時你才應該使用它, 比如, 一個執行時間很短的終端程式。 對於大多數人, 懶載入都是沒有必要的,
舊方法
之前我們有兩種方式做懶載入。 最古老的方式是在局部載入, 而不是全域載入(例如在你定義的函數內進行導入, 而不是在模組頂部進行導入)。 這種方式確實推遲了載入的時間點, 在你的函數被運行時, 函數裡的import語句才會進行真正的載入。 但它有一個缺點就是, 這個import語句需要在不同的函數中寫很多次。 而且由於你只是在一些函數中寫了import語句, 你很可能寫著寫著就忘記了想要規避哪個模組的全域引用, 然後後面又不小心全域引用了同樣的模組。 這個做法確實能實現懶載入, 就是寫法不夠好。
另一種方法是使用importlib中提供的延遲載入器。
很多人很喜歡這個延遲載入器, 以至於他們讓所有的東西都延遲載入了。 這樣做有優點也有缺點。 優點是你沒有額外付出什麼努力, 就讓所有的模組都延遲載入了。 缺點是因為你讓模組預設延遲載入了, 會導致一些需要即時載入的模組的邏輯發生錯誤(這也就是Python箴言中為什麼說明確優於隱晦)。 事實上, Mercurial為了避免這個問題,
新方法
在Python3.7中, 模組上可以定義一個__getattr__方法, 這讓開發者可以定義一個函數, 使得訪問的模組屬性不存在時, 導入一個模組作為當前模組的屬性。 這樣做確實也有“發現導入錯誤被推遲”這個弊病, 但是由於你的導入還是全域的, 所以代碼更容易控制。
這個代碼本身並不複雜。
你可以這樣使用上面的代碼
設計這個函數時, 最棘手的部分就是類比import ... as ... 語法來避免命名衝突, 我最終選擇使用一個類似原有as語法的字串。 我也可以把as語法字串再拆分為第三個參數, 這個參數也是一個字典物件, 但是我想沒必要這樣做, 能與原有語法有更多的相同點, 當然是最好的。
無論如何, 這個思考的過程都讓我很享受。 我喜歡這種用20行Python代碼就完成一個不錯的功能的感覺!
譯者:詩書塞外
英文原文:https://snarky.ca/lazy-importing-in-python-3-7/