如何将多个Python源文件合并为一个文件?

4
<假设: 应用程序启动时间非常关键; 我的应用程序经常启动; 我的应用程序运行在一个导入速度比平时慢的环境中; 有许多文件需要导入; 并且不能编译为 .pyc 文件。> 我想把定义一组模块的所有Python源文件连接成一个新的Python源文件。
我希望导入新文件的结果与导入原始文件中的一个文件相同 (然后原始文件将导入更多的原始文件,以此类推)。
这是否可行?
下面是对工具输入 'bar' 和 'baz' 模块源文件的粗略手动模拟,这样的工具在部署代码之前运行。
__file__ = 'foo.py'

def _module(_name):
    import types
    mod = types.ModuleType(name)
    mod.__file__ = __file__
    sys.modules[module_name] = mod
    return mod

def _bar_module():

    def hello():
        print 'Hello World! BAR'

    mod = create_module('foo.bar')
    mod.hello = hello
    return mod

bar = _bar_module()
del _bar_module

def _baz_module():

    def hello():
        print 'Hello World! BAZ'

    mod = create_module('foo.bar.baz')
    mod.hello = hello
    return mod

baz = _baz_module()
del _baz_module

现在您可以:

from foo.bar import hello
hello()

这段代码没有考虑到导入语句和依赖项等因素。是否有现成的代码可以使用此技术或其他技术来组装源文件?

这与在发送到浏览器之前使用工具组装和优化JavaScript文件的想法非常相似,其中多个HTTP请求的延迟会影响性能。在Python的情况下,启动时导入数百个Python源文件的延迟会影响性能。


6
你有任何数据可以证明这将影响口译启动时间吗? - Azeem.Butt
启动时间肯定是一个问题。我希望这能加快速度。我已经在问题中澄清了限制。 - Your Mom's Mom
1
我也怀疑你能在这里看到速度提升。每次重新加载应用程序时,您仍需要将 X 行代码加载到 VM 中。在将模块统一为一个大文件之前,我肯定会投入时间确认是否可以加快速度。 - JasonSmith
"在将您的模块统一到一个大文件之前,请先考虑一下。" 您错过了最后一行:"是否有任何现有代码,可以使用此技术或其他技术组装源文件?" 我不是提议像这样编写源代码,我希望在部署之前处理现有的源代码。我也希望这是一种已经有人尝试过的技术,这样我就不必自己编写它 :-) - Your Mom's Mom
4个回答

3
如果这是在 Google 应用引擎上,正如标签所示,请确保您使用此习语。
def main(): 
    #do stuff
if __name__ == '__main__':
    main()

因为GAE只有在.py文件改变的情况下才会重新启动应用程序,它仅仅再次运行main()。这个技巧让你编写CGI风格的应用程序而不会受到启动性能的影响。 AppCaching 如果处理程序脚本提供了一个main()例程,运行时环境还会缓存该脚本。否则,每个请求都会加载处理程序脚本。

谢谢,但我已经这样做了。请注意,应用引擎通常只缓存一秒钟左右,每天会有大约14,000次缓存未命中的机会,当使用许多具有复杂导入依赖关系的模块时,情况会更加恶化。我真的很关心最大的启动性能。 - Your Mom's Mom

1

我认为由于Python文件的预编译和一些系统缓存,你最终得到的加速效果是无法衡量的。


你的意思是“不可测量”吗? - Bryan Oakley
预编译为 .pyc 文件不可用。在这种特定情况下,缓存并不像我希望的那样好。我已经在问题中澄清了这一点。 - Your Mom's Mom

0

这样做不太可能带来任何性能优势。你仍然会导入同样数量的Python代码,只是在更少的模块中 - 而且你为此牺牲了所有的模块化。

更好的方法是修改你的代码和/或库,只在需要时导入东西,以便每个请求加载最少量的必需代码。


不确定您从何处得到代码是“按需”部署的想法 - 您可以使用os.listdir等函数来查看这不是情况。 您是否在说由数万名开发人员编写的数万个应用程序同时并持久地部署到每个应用引擎集群中数千台服务器上的普通块大小,ext2 / 3/4文件系统?也就是说,应用引擎生产环境与平均服务器设置基本相似?我不明白在这种规模下如何可能... - Your Mom's Mom
如果拥有“300多个”源文件在需要按需导入时是一个问题的话,将它们全部合并也同样有问题... 这是一个关于批处理的问题。使用db.Get([键列表])比使用for key in list_of_keys: db.Get(key)更加高效;例如。 - Your Mom's Mom
你的整个应用程序已部署到运行它的服务器上。你的优化基于一个不适用的前提。 - Nick Johnson
很好。最近在生产服务器上做了哪些更改?曾经导入单个模块会导致30秒的超时,然后添加了优化后导入变得比之前更快,甚至比30秒超时前还要快?如果确实是如此基本的设置,那么在导入性能方面出现如此大的差异的范围在哪里呢? - Your Mom's Mom
这是一个bug。我不能详细说明原因,但现在已经修复了。 - Nick Johnson
显示剩余8条评论

0

不考虑这种技术是否能够提高您的环境,假设您是正确的,以下是我会做的。

我会列出所有我的模块,例如: my_files = ['foo', 'bar', 'baz']

然后,我将使用 os.path 工具读取源目录下所有文件中的所有行,并将它们全部写入一个新文件中,过滤掉所有的 import foo|bar|baz 行,因为现在所有代码都在单个文件中。

当然,最后将 __init__.py 中的 main()(如果有的话)添加到文件的末尾。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接