跳转到主要内容

用于加速模块导入的懒惰和自毁工具

项目描述

一个提供用于加速模块导入的懒惰和自毁工具的包。这在启动时间至关重要的任何时候都很有用,例如命令行界面或其他面向用户的应用程序。

此模块中的工具实现了两种不同的策略来加速模块导入。第一种是延迟构建全局状态,第二种是在后台线程中导入昂贵的模块。

您可以将lazyasd用作依赖项,或者因为它是一个单独的模块实现,所以可以将lazyasd.py文件复制到您的项目中。

懒惰构建

与数据构建或检查设置相关的许多操作可能需要很长时间才能完成。如果只需要数据的一个副本或缓存表示,在Python中通常会将数据移动到全局或模块级别作用域。

通过移动到模块级别,我们帮助确保永远不会构建数据的一个副本。然而,通过移动到模块作用域,单次性能打击现在发生在导入时间。如果数据从未使用过,这本身就是一种浪费。此外,构建的全局数据越多,导入模块所需的时间越长。

例如,考虑一个使用正则表达式报告字符串是否包含单词"foo"的函数。由于每次函数调用都要构造正则表达式,因此原始版本相对较慢。

import re

def has_foo_simple(s):
    return re.search('foo', s) is not None

提高性能的标准方法是编译全局作用域中的正则表达式。重写后,我们会看到

import re

FOO_RE = re.compile('foo')

def has_foo_compiled(s):
    return FOO_RE.search(s) is not None

现在,每次调用 has_foo_compiled() 都比调用 has_foo_simple() 快得多,因为我们已经将编译移到了导入时。但是,如果我们根本没调用过 has_foo() 呢?在这种情况下,原始版本更好,因为导入很快。

要同时拥有编译一次和导入时不编译的最好之处,就需要使用懒惰和自我毁灭性的工具。一个 LazyObject 实例有一个加载函数,一个放置结果的上下文,以及加载值在上下文中的名称。当 LazyObject 首次创建时,它不会做任何工作。然而,每当访问一个属性(或各种其他操作)时,加载器将被调用,构造出真正的值,并且 LazyObject 将充当加载对象的代理。

使用上述正则表达式示例,以下懒惰实现具有最小化的导入时间和运行时性能损失

import re
from lazyasd import LazyObject

FOO_RE = LazyObject(lambda: re.compile('foo'), globals(), 'FOO_RE')

def has_foo_lazy(s):
    return FOO_RE.search(s) is not None

下面我们来逐步分析上述代码,在导入时 FOO_RE 是一个 LazyObject,它有一个返回我们关心的正则表达式的 lambda 加载器。如果 FOO_RE 从未被访问,它将保持这种方式。然而,第一次调用 has_foo_lazy() 时,访问 search 方法将导致 LazyObject 执行以下操作:

  1. 调用加载器(得到 re.compile('foo') 作为结果)

  2. 将结果放置在上下文中,例如 globals()['FOO_RE'] = re.compile('foo')

  3. 在结果上查找属性和方法(如 search)。

现在,由于上下文替换,FOO_RE 现在是一个正则表达式对象。后续对 has_foo_lazy() 的调用将直接看到 FOO_RE 作为正则表达式对象,而不是作为 LazyObject。实际上,如果没有遗留引用,原始的 LazyObject 实例可以被垃圾收集器完全清理掉!

对于真正懒惰的情况,还有一个 lazyobject 装饰器

import re
from lazyasd import lazyobject

@lazyobject
def foo_re():
    return re.compile('foo')

def has_foo_lazy(s):
    return foo_re.search(s) is not None

另一个有用的模式是实现懒惰模块导入,只有当使用模块的成员时才导入模块

import importlib
from lazyasd import lazyobject

@lazyobject
def os():
    return importlib.import_module('os')

世界美好如你,但不妨先小憩片刻。

具体懒惰

LazyBool 类和 lazybool 装饰器具有与懒惰对象相同的接口。这些是为了那些打算解析为布尔值的对象而提供的。

LazyDict 类和 lazydict 装饰器相似。然而,这里的第一值是一个键加载器的字典。而不是有一个单一的加载器,每个值在其键首次访问时单独加载。

后台导入

即使有所有的懒惰性,有时这也还不够。有时导入一个模块非常痛苦且不可避免,需要在后端线程中导入它,以便在此期间其他应用程序可以启动。这就是 load_module_in_background() 的目的。

例如,如果你正在使用 pygments 并且想将导入速度提高 100 倍,只需添加以下行:

# must come before pygments imports
from lazyasd import load_module_in_background
load_module_in_background('pkg_resources',
                          replacements={'pygments.plugin': 'pkg_resources'})

# now pygments is fast to import
from pygments.style import Style

这阻止了来自 setuptools 的 pkg_resources 在导入时在整个文件系统中搜索插件。像上面一样,这个导入作为代理并将在需要时才阻塞。如果模块已经导入,它也足够健壮。在某些情况下,这种后台导入是第三方应用程序能做的最好的。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源代码分发

lazyasd-0.1.4.tar.gz (8.4 kB 查看哈希值)

上传时间 源代码

支持