- はじめに -
この記事は、Xonsh Advent Calendar 2017 - Qiita 18日目の記事です。
Pythonのmoduleのloadを実際に利用する前に遅延してやろうというTipsです。
加えて、xonshのxonshrcに記載する事で、xonshの起動も早くしようという話を書いています。
- 遅延importを実現する -
遅延させて、使いたい時にpythonスクリプトをロードしたりするには大抵importlibを使います。
そこでimportlibでxonshの起動爆速化を狙おうとしていた所、同僚に「lazyasdっていうのがあるよ」と言われて調べたら大体それで出来たので、これで良いやという感じでした。
以下には一応どちらも記述しています。
importlibで動的ロード -
Pythonでモジュールをスクリプト上で動的にロードするにはimportlibを使います。
31.5. importlib — The implementation of import — Python 3.6.6rc1 documentation
import importlib os = importlib.import_module("os") print(os.listdir("."))
machineryを使えばimportのタイミングをhookできます。
https://docs.python.org/3.6/library/importlib.html#module-importlib.machinery
遅延してロードさせる場合は、Python3.5にて追加されたimportlib.util.LazyLoaderを使うと良いです。
31.5. importlib — The implementation of import — Python 3.6.6rc1 documentation
参考:Python: モジュールを動的にロードする - CUBE SUGAR CONTAINER
lazyasdを使った遅延import
同僚が「xonsh使ってるならxonsh.lazyasdの中にLazyObjectやBackgroundModuleLoaderがあるのでそれ使うと良いよ」と教えてくれました。
Lazy & Self-destructive Objects (xonsh.lazyasd) — xonsh 0.6.7 documentation
その後、調べてみるとlazyasdだけパッケージとして切り離されているようです。
GitHub - xonsh/lazyasd: Lazy & self-destructive tools for speeding up module imports
pipでinstallできるのでやります
pip install lazyasd
lazyasdで一番簡易にモジュール使う時ロードを実現できるのはデコレータを付けることです。
import importlib from lazyasd import lazyobject @lazyobject def os(): return importlib.import_module('os') # importが発生するのはココ print(os.listdir("."))
実際にモジュールを利用する時にimportする事が割りと簡単にできました。
compile済みの正規表現も遅延させて読み込めます。
import re from lazyasd import lazyobject @lazyobject def hoge_re(): return re.compile('hoge') print(hoge_re.search("hoge piyo str") is not None)
より良い方法として、別スレッドで読み込むload_module_in_backgroundもあります。
from lazyasd import load_module_in_background os = load_module_in_background('os') print(os.listdir("."))
引数のreplacementsも便利です。
関数を引数に取るためlambda使ったりとちょっと面倒ですが、LazyObjectやLazyDictといったClassも用意されています。
from lazyasd import LazyObject from lazyasd import LazyDict import re # 最初にosを使う時にglobalにosをimportする os = LazyObject(lambda: importlib.import_module('os'), globals(), 'os') print(os.listdir(".")) # 一回目に使う時に正規表現をcompileする RES = LazyDict({ 'dot': lambda: re.compile('.'), 'all': lambda: re.compile('.*'), 'two': lambda: re.compile('..'), }, globals(), 'RES') print(RES["dot"].search("hogehoge.text") is not None)
「使うか分からないので、もし使うなら最初にロードしたいな」という時に使えます。
多分以下を見るかソースコードを見ると良いです。
Lazy & Self-destructive Objects (xonsh.lazyasd) — xonsh 0.6.7 documentation
- xonshrcに書いていく -
ここからはxonshrcに書いておいてxonsh起動までを爆速にしてやろうという話です。
xonshであればpip installする必要もなく、xonsh.lazyasdを利用できます。
使うか分からないがimportを忘れがちなやつを全部こうしてやります
from xonsh.lazyasd import lazyobject @lazyobject def os(): return importlib.import_module('os')
全部listに突っ込んでおけば、execしてくれるSampleです。
import asのようにしたい場合は、dictか何かにしてformat第一引数xをよしなにやれば良いです
from xonsh.lazyasd import lazyobject import importlib # list版 lazy_module_list = ["requests", "numpy", "pandas", "matplotlib",...] for x in lazy_module_list: t = "@lazyobject\ndef {}():\n return importlib.import_module('{}')".format(x, x) exec(t) # dictならこんな感じか lazy_module_dict = { 'requests': 'requests', 'sys': 'sys', 'random': 'random', 'shutil': 'shutil', 'pd': 'pandas', 'np': 'numpy', 'plt': 'matplotlib.pyplot', 'Path': 'pathlib.Path', } for k,v in lazy_module_dict.items(): t = "@lazyobject\ndef {}():\n return importlib.import_module('{}')".format(k, v) exec(t)
これをxonshrcに書いて優勝です
いやいや、絶対コンソール使ってたら使うでしょという物はbackgroundでimportしてやりましょう。
from lazyasd import load_module_in_background background_module_list = ["os", "sys", "random", "shutil", "linecache",...] for x in background_module_list: exec("{}=load_module_in_background('{}')".format(x, x))
参考:Lazy & Self-destructive Objects (xonsh.lazyasd) — xonsh 0.6.7 documentation
- おわりに -
lazyasd便利なので、環境に合わせてロードするやつとかも拡張として書いていきたい所。
importlibのLoader周りはあんまりExamplesもなくて厳しいですが、まあなんとか。
Xonshアドベントカレンダーの方もよろしくお願いします。
Xonsh Advent Calendar 2017 - Qiita