延迟加载Python模块的最佳实践

max*_*max 38 python coding-style

偶尔我想在Python中加载延迟模块.通常因为我想保持运行时间要求或启动时间较低并将代码拆分为子模块会很麻烦.一个典型的用例和我目前首选的实现是这样的:

jinja2 = None

class Handler(...):
    ...
    def render_with_jinja2(self, values, template_name):
        global jinja2
        if not jinja2:
            import jinja2
        env = jinja2.Environment(...)
        ...
Run Code Online (Sandbox Code Playgroud)

我想知道:有没有规范/更好的方法来实现懒惰模块加载?

bgp*_*ter 59

您没有理由手动跟踪导入 - VM维护已导入的模块列表,并且导入该模块的任何后续尝试都会导致在sys.modules中进行快速dict查找而不执行任何其他操作.

您的代码与之间的区别

def render_with_jinja2(self, values, template_name):
    import jinja2
    env = jinja2.Environment(...)
Run Code Online (Sandbox Code Playgroud)

为零 - 当我们点击该代码时,如果jinja2尚未导入,则导入该代码.如果已经存在,则继续执行.

  • 有时更多的单词是细节,但有时更多的单词只是更多的单词.:) (4认同)
  • @mdorseif - 有两个不同的问题.(1)是的,jinja2名称仅在该函数的范围内可见,但是(2)jinja2模块仍然缓存在sys.modules中,因此额外的导入(在后续调用该函数或其他任何地方)实际上是无操作.我假设你打算把那个'全球jinja2; 如果不是jinja2:'代码在多个功能? (4认同)
  • @Glenn - 太多咖啡,不能停止打字! (3认同)
  • 还有一个明显的区别是,您的代码仅在本地范围内拉取 jinja2 - 还是我在这里遗漏了什么? (3认同)
  • @mdorseif:`jinja2 = None` 不好的原因是因为它太不寻常了。当我看到那行时,我不认为“他稍后会导入它,jinja2 是一个模块”。看起来您正在将全局变量初始化为 None。我对您的意图有任何暗示的唯一原因是因为“jinja2”是社区中众所周知的模块名称。如果我看到“chemistry3 = None”,我就不知道你在做什么。在函数中本地导入模块,这是最好的方法。(续) (2认同)
  • (续)如果您想让浏览者知道发生了什么,请在顶部添加注释,例如“# 我在整个代码中使用 jinja2,但将其导入本地以避免启动时间过长。” (2认同)

Gle*_*ard 23

class Handler(...):
    ...
    def render_with_jinja2(self, values, template_name):
        import jinja2
        env = jinja2.Environment(...)
        ...
Run Code Online (Sandbox Code Playgroud)

无需缓存导入的模块; Python已经做到了.

  • PyCon上的Unladen Swallow谈话指出,对于子包导入(与这种情况不同),在sys.modules中解析和查找导入级别的工作量是合理的,所以不要假设导入的频率很低 - 被称为函数(他的性能提升了20%,优化了django函数) (5认同)
  • @mdorself最好的做法是你摇摆的东西. (3认同)
  • 缺点是你不能只查看 py 文件的顶部并查看它使用的模块,但如果你认为有必要,你可以添加注释。然而,这肯定比 OP 所做的更好/更干净 (2认同)

Nou*_*him 10

其他答案已经覆盖了实际的细节,但如果你兴趣在一个延迟加载库,检查出apipkg这是金py包(py.test成名).