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

延迟加载Python模块的最佳实践,python,coding-style,Python,Coding Style,有时我希望在Python中加载惰性模块。通常是因为我想降低运行时需求或启动时间,而将代码拆分为子模块会很麻烦。一个典型的用例和我目前首选的实现是: jinja2 = None class Handler(...): ... def render_with_jinja2(self, values, template_name): global jinja2 if not jinja2: import jinja2

有时我希望在Python中加载惰性模块。通常是因为我想降低运行时需求或启动时间,而将代码拆分为子模块会很麻烦。一个典型的用例和我目前首选的实现是:

jinja2 = None

class Handler(...):
    ...
    def render_with_jinja2(self, values, template_name):
        global jinja2
        if not jinja2:
            import jinja2
        env = jinja2.Environment(...)
        ...
我想知道:有没有一种规范的/更好的方法来实现延迟模块加载

class Handler(...):
    ...
    def render_with_jinja2(self, values, template_name):
        import jinja2
        env = jinja2.Environment(...)
        ...

不需要缓存导入的模块;Python已经做到了这一点。

您没有理由手动跟踪导入—VM维护一个已经导入的模块列表,任何后续导入该模块的尝试都会导致在sys.modules中进行快速dict查找,而不会产生其他任何结果

您的代码和

def render_with_jinja2(self, values, template_name):
    import jinja2
    env = jinja2.Environment(...)

是零——当我们点击该代码时,如果没有导入
jinja2
,那么它就会被导入。如果已经执行,则继续执行

其他答案涵盖了实际细节,但如果您对延迟加载库感兴趣,请查看
py
包(
py.test
fame)中的哪一部分

sqlalchemy的好模式:依赖注入:

@util.dependencies("sqlalchemy.orm.query")
def merge_result(query, *args):
    #...
    query.Query(...)
它不会在模块顶部声明所有“import”语句,而是只在函数实际需要时导入模块。
这可以解决循环依赖性问题。

有时更多的单词就是细节,但有时更多的单词就是更多的单词。:)@格伦——咖啡喝得太多了,不能停止打字!另一个明显的区别是,您只在本地范围内编写jinja2,或者我在这里遗漏了什么?@mdorseif——有两个不同的问题。(1) 是的,jinja2名称仅在该函数的作用域内可见,但(2)jinja2模块仍缓存在sys.modules中,因此(在随后调用该函数或其他任何地方时)其他导入实际上是无效的。我猜你是在计划把“全球金杯2号”;如果不是jinja2:“在多个函数中编码?”@mdorseif:jinja2=None的原因是它太不寻常了。当我看到这一行时,我不认为“他稍后会导入它,jinja2是一个模块”。看起来您正在将全局变量初始化为“无”。我对您的意图有所了解的唯一原因是“jinja2”是社区中一个众所周知的模块名称。如果我看到
chemical3=None
,我就不知道你在干什么。在函数中本地导入模块,这是最好的方法。(续)嗯。我想我听说将模块放入本地范围不被认为是最佳做法-尽管我不知道为什么。@mdorself更好的做法是你所做的一切。@aaronastering想详细说明一下吗?缺点是你不能只看py文件的顶部,看看它使用了什么模块,但如果你认为有必要,你可以发表评论。这当然比OP所做的更好/更干净,但是PyCon的Unladen Swallow talk指出,对于子包导入(与本例不同),在sys.modules中解析和查找导入级别需要做大量的工作,因此不要假设导入在频繁调用的函数中是便宜的(通过优化django函数,他获得了20%的性能提升)它只允许全局加载一次(而不是在27个可能发生第一次调用
jinja2
的地方),并且在尝试第一次访问时(并且仅在尝试第一次访问时)自动执行实际加载。(不要在C++中尝试)哦,这太神奇了。它甚至不尝试加载,直到你访问了懒惰加载的东西上的某个属性。所以,甚至从MyPKG导入路径< />代码> <代码>(从例子中)在尝试访问
路径上的内容之前,不会导入任何内容。延迟加载的另一个原因可能是因为它是一个写得不好的第三方模块,需要在导入之前更改全局环境设置。(当然,这些模块非常有趣。)