我需要从zipfile(由py2exe压缩构建)读取所有模块(预编译)到内存中然后加载它们。 我知道这可以通过从zipfile直接加载来完成,但我需要从内存中加载它们。 有任何想法吗? (我在Windows上使用python 2.5.2) TIA Steve
答案 0 :(得分:31)
这取决于你所拥有的“模块(预编译)”。我们假设它正是.pyc
文件的内容,例如,ciao.pyc
由以下内容构建:
$ cat>'ciao.py'
def ciao(): return 'Ciao!'
$ python -c'import ciao; print ciao.ciao()'
Ciao!
我已经建立了ciao.pyc
,说你现在这样做了:
$ python
Python 2.5.1 (r251:54863, Feb 6 2009, 19:02:12)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> b = open('ciao.pyc', 'rb').read()
>>> len(b)
200
,您的目标是从该字节字符串b
转到可导入模块ciao
。方法如下:
>>> import marshal
>>> c = marshal.loads(b[8:])
>>> c
<code object <module> at 0x65188, file "ciao.py", line 1>
这是从.pyc
二进制内容中获取代码对象的方法。 编辑:如果你很好奇,前8个字节是一个“幻数”和时间戳 - 这里不需要(除非你想要理智地检查它们并在有保证的情况下引发异常,但是似乎超出了问题的范围;如果检测到损坏的字符串,marshal.loads
无论如何都会提高。)
然后:
>>> import types
>>> m = types.ModuleType('ciao')
>>> import sys
>>> sys.modules['ciao'] = m
>>> exec c in m.__dict__
即:创建一个新的模块对象,将其安装在sys.modules
中,通过在__dict__
中执行代码对象来填充它。 修改:sys.modules
插入和exec
的顺序,当且仅当您可能有循环导入时 - 但是,这是Python自己的{{}} {{ 1}}通常使用,因此最好模仿它(没有特定的缺点)。
您可以通过多种方式“制作新的模块对象”(例如,来自import
和new
等标准库模块中的函数,但是“调用类型来获取实例”是这些天正常的Python方式,以及从中获取类型的正常位置(除非它有内置名称,否则你已经很方便)来自标准库模块imp
,这就是我推荐的
现在,最后:
types
...您可以导入模块并使用其功能,类等。然后,其他>>> import ciao
>>> ciao.ciao()
'Ciao!'
>>>
(和import
)语句将找到模块from
,因此您无需重复此操作序列(实际上您不需要 / em>这里的最后一个sys.modules['ciao']
语句如果你想要的是确保模块可以从其他地方导入 - 我只是为了表明它有用而添加它; - )。
编辑:如果你绝对必须以这种方式导入包和模块,而不是像我刚刚展示的“普通模块”那样,那也是可行的,但有点复杂。由于这个答案已经很长了,我希望你可以通过坚持使用普通模块来简化你的生活,我会推卸答案的那一部分; - )。
另请注意,在“多次从内存加载相同模块”的情况下,这可能会或可能不会执行您想要的操作(每次重建模块;您可能需要检查sys.modules并且只是跳过所有内容,如果模块已经存在),特别是当从多个线程(需要锁定)发生这样的重复“从内存加载”时 - 但是,更好的架构是有一个专用线程专门用于执行任务,其他模块通过队列)。
最后,没有讨论如何将此功能安装为透明的“导入钩子”,它自动参与import
语句内部机制本身 - 这也是可行的,但不完全是你的我在这里问,所以在这里,我希望你可以通过简单的方式来简化你的生活,正如这个答案所概述的那样。
答案 1 :(得分:9)
编译后的Python文件由
组成要加载模块,您必须使用imp.new_module()
创建模块对象,在新模块的命名空间中执行未编码的代码并将其放在sys.modules
中。以下是示例实现:
import sys, imp, marshal
def load_compiled_from_memory(name, filename, data, ispackage=False):
if data[:4]!=imp.get_magic():
raise ImportError('Bad magic number in %s' % filename)
# Ignore timestamp in data[4:8]
code = marshal.loads(data[8:])
imp.acquire_lock() # Required in threaded applications
try:
mod = imp.new_module(name)
sys.modules[name] = mod # To handle circular and submodule imports
# it should come before exec.
try:
mod.__file__ = filename # Is not so important.
# For package you have to set mod.__path__ here.
# Here I handle simple cases only.
if ispackage:
mod.__path__ = [name.replace('.', '/')]
exec code in mod.__dict__
except:
del sys.modules[name]
raise
finally:
imp.release_lock()
return mod
更新:更新代码以正确处理包。
请注意,您必须安装导入挂钩才能处理已加载模块中的导入。一种方法是将您的查找器添加到sys.meta_path
。有关详细信息,请参阅PEP302。