用于加速模块导入的懒惰和自毁工具
项目描述
一个提供用于加速模块导入的懒惰和自毁工具的包。这在启动时间至关重要的任何时候都很有用,例如命令行界面或其他面向用户的应用程序。
此模块中的工具实现了两种不同的策略来加速模块导入。第一种是延迟构建全局状态,第二种是在后台线程中导入昂贵的模块。
您可以将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 执行以下操作:
调用加载器(得到 re.compile('foo') 作为结果)
将结果放置在上下文中,例如 globals()['FOO_RE'] = re.compile('foo')
在结果上查找属性和方法(如 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的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | a3196f05cff27f952ad05767e5735fd564b4ea4e89b23f5ea1887229c3db145b |
|
MD5 | c510082473c81fa9e1eafddf02eec41a |
|
BLAKE2b-256 | b315c9f68c4f477fbaa9fc5ea5e271547956da321787cf7aef79d6971dcb4c84 |