如何导入 .pyc 编译的 python 文件并使用它

Ke.*_*Ke. 2 python import file include

我试图弄清楚如何.pyc在 python 脚本中包含一个文件。

例如我的脚本被称为:

myscript.py
Run Code Online (Sandbox Code Playgroud)

我想包含的脚本被称为:

included_script.pyc
Run Code Online (Sandbox Code Playgroud)

那么,我是否只使用:

import included_script
Run Code Online (Sandbox Code Playgroud)

那会自动执行included_script.pyc吗?或者我还需要做些什么才能让我included_script.pycmyscript.py.

我还需要传递使用的变量included_script.pyc吗?如果是这样,如何实现?

Jim*_*ard 7

不幸的是,,这不能自动完成。当然,您可以以非常丑陋的方式手动完成


设置:

出于演示目的,我将首先生成一个.pyc文件。为了做到这一点,我们首先需要一个.py文件。我们的示例test.py文件将如下所示:

def foo():
    print("In foo")

if __name__ == "__main__":
    print("Hello World")
Run Code Online (Sandbox Code Playgroud)

超级简单。.pyc可以使用py_compile标准库中的模块生成文件。我们只需通过以下方式传入.py文件名和文件名.pyc

 py_compile.compile('test.py', 'mypyc.pyc')
Run Code Online (Sandbox Code Playgroud)

这将放置mypyc.pyc在我们当前的工作目录中。


.pyc文件中获取代码:

现在,.pyc文件包含按以下方式构造的字节:

  • 前 4 个字节表示“幻数”
  • 接下来的 4 个字节保存修改时间戳
  • 其余内容是编组 code对象。

我们所追求的是那个编组的code对象,所以我们需要解组import marshal它并执行它。此外,我们真的不关心/需要第 8 个字节,并且.pyc不允许使用它们对文件进行解组,因此我们将忽略它们(seek过去它们):

import marshal

s = open('mypyc.pyc', 'rb')
s.seek(8)  # go past first eight bytes
code_obj = marshal.load(s)
Run Code Online (Sandbox Code Playgroud)

所以,现在我们有我们的幻想code对象,test.py它是有效的,可以按照我们的意愿执行。我们在这里有两个选择

  1. 在当前global命名空间中执行它。这将绑定.pyc当前命名空间中我们文件中的所有定义,并将作为一种:from file import *语句。

  2. 创建一个新的模块对象并执行模块内的代码。这将类似于import file声明。


模仿from file import *类似的行为:

执行此操作非常简单,只需执行以下操作:

exec(code_obj)
Run Code Online (Sandbox Code Playgroud)

这将执行code_obj当前命名空间中包含的代码并将所有内容绑定到那里。在调用之后,我们可以foo像任何其他函数一样调用:

foo()
# prints: In foo!
Run Code Online (Sandbox Code Playgroud)

注意exec()是内置的。


模仿import file类似的行为:

这包括另一个要求,即types模块。这包含ModuleType我们可以用来创建新模块对象的类型。它需要两个参数,模块的名称(必需)和它的文档(可选):

m = types.ModuleType("Fancy Name", "Fancy Documentation")

print(m)
<module 'Fancy Name' (built-in)>
Run Code Online (Sandbox Code Playgroud)

现在我们有了我们的模块对象,我们可以再次使用exec来执行包含在code_obj模块命名空间(即m.__dict__)中的代码:

exec(code_obj, m.__dict__)
Run Code Online (Sandbox Code Playgroud)

现在,我们的模块m在 中定义了所有内容code_obj,您可以通过运行来验证这一点:

m.foo() 
# prints: In foo
Run Code Online (Sandbox Code Playgroud)

这些是您可以.pyc在模块中“包含”文件的方法。至少,我能想到的方法。我并没有真正看到它的实用性,但是嘿,我不是来判断的。

  • 在python 3.7及以上版本中,我相信应该是s.seek(16)。https://www.python.org/dev/peps/pep-0552/ (3认同)
  • 虽然我没有太多信心,但Python 3.3+似乎需要另外4个字节在head magic munbers中,所以s.seek(8)需要是s.seek(12)。https://qiita.com/amedama/items/698a7c4dbdd34b03b427 (2认同)