您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

如何从内存加载已编译的python模块?

如何从内存加载已编译的python模块?

这取决于您所拥有的“模块(预编译)”的确切含义。假设它恰好是.pyc文件内容,例如,Ciao.pyc通过以下方式构建:

$ cat>'Ciao.py'
def Ciao(): return 'Ciao!' 
$ python -c'import Ciao; print Ciao.Ciao()'
Ciao!

IOW这样构建后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自己import通常使用的顺序,因此最好模仿它(没有特定的缺点)。

您可以通过几种方式“创建新的模块对象”(例如,从标准库模块中的函数,如newimp),但是“调用类型以获取实例”是当今的常规Python方式,也是获取常规位置的常用方法。 from的类型(除非它具有内置名称,否则您已经很方便了)来自标准库模块types,因此,我建议这样做。

现在,最后:

>>> import Ciao
>>> Ciao.Ciao()
'Ciao!'
>>>

…您可以导入模块并使用其功能,类等。然后,其他import(和from)语句将找到该模块sys.modules['Ciao'],因此您无需重复此操作序列(实际上,如果您只是想确保该模块可从其他位置导入,则在此不需要 最后一条import语句) -我添加它只是为了表明它有效;-)。

:如果您绝对必须以这种方式从中导入包和模块,而不是像我刚才所示的“纯模块”,那也是可行的,但是稍微复杂一点。由于这个答案已经很长了,我希望您可以通过为此使用简单的模块来简化您的生活,我将回避答案的那一部分;-)。

还要注意,在“多次从内存中加载同一模块”的情况下,这可能会或可能不会做您想要的(每次都会重新构建模块;您可能要检查sys.modules,如果模块已经存在,则跳过所有内容),尤其是当这种重复的“从内存中加载”是从多个线程中发生时(需要锁- 但更好的体系结构是只有一个专用线程来执行任务,而其他模块则通过队列与之通信)。

最后,没有讨论如何将此功能安装为透明的“导入钩子”,该钩子自动地参与了import语句内部结构本身的机制- 这也是可行的,但并不完全是您要问的,所以在这里,我也希望您能通过简单的方法来简化生活,如答案所示。

python 2022/1/1 18:47:29 有337人围观

撰写回答


你尚未登录,登录后可以

和开发者交流问题的细节

关注并接收问题和回答的更新提醒

参与内容的编辑和改进,让解决方法与时俱进

请先登录

推荐问题


联系我
置顶