在Python 3.4中无源文件运行

29 投票
2 回答
11091 浏览
提问于 2025-04-18 16:20

我想运行一个Python应用程序,但不想保留.py源文件,只依赖.pyc编译后的文件。然而,当我删除.py源文件时,出现了导入错误。在Python 2.7中,这个功能是可以正常工作的,但在3.4中(使用新的__pycache__结构)就不行了。

这里有一个示例目录结构:

package/
  __init__.py
  module.py

Python 2.7

首先,让我们看看在Python 2.7中会发生什么(这是我想要的效果)

$ python2 -c "from package import module"
$ find package -name "*.py" -delete
$ python2 -c "from package import module"

一切正常,没有错误。执行完后,目录结构看起来是这样的,.pyc文件和原始的.py文件在一起:

package/
  __init__.pyc
  module.pyc

Python 3.4

现在,让我们用Python 3.4做同样的事情,从我们的原始目录结构开始

$ python3 -c "from package import module"
$ find package -name "*.py" -delete
$ python3 -c "from package import module"
 Traceback (most recent call last):
   File "<string>", line 1, in <module>
 ImportError: cannot import name 'module'

哦,不,它无法导入模块。有趣的是,此时我仍然可以安全地运行python3 -c "import package",但它无法从那里抓取任何模块。此时,目录结构看起来和2.7有点不同,具体是这样的:

package/
  __pycache__/
      __init__.cpython-34.pyc
      module.cpython-34.pyc

所以问题是:为什么Python 3.4在只有.pyc文件的情况下无法正确导入/执行?这是预期的行为吗?也就是说,源文件在所有情况下都必须保留?还是我漏掉了什么简单的东西?

2 个回答

37

因为没有足够的声望来给BrenBarn的回答添加评论,所以我在这里补充一些内容。

根据compileall的文档:

-b

这个选项会把字节码文件写到它们的旧位置和名称,这可能会覆盖由其他版本的Python创建的字节码文件。默认情况下,它会把文件写到PEP 3147指定的位置和名称,这样不同版本的Python生成的字节码文件就可以共存。

所以你可以运行python -m compileall -b .来递归编译这个目录下的所有代码文件。

34

根据PEP文档的说明:

有可能是foo.py这个文件被删除了,但缓存的pyc文件还留在文件系统里。如果__pycache__/foo.<magic>.pyc文件存在,但创建它的foo.py文件却不见了,当你尝试导入foo时,Python会报错,提示找不到这个模块。换句话说,Python不会从缓存目录导入pyc文件,除非源文件存在。

但是:

为了继续支持没有源文件的分发方式,如果源文件缺失,Python会导入一个单独的pyc文件,只要它放在源文件应该在的位置。

所以看起来__pycache__和没有源文件的分发方式是互相排斥的。如果你想删除源文件,就需要把.pyc文件移动到源文件应该在的目录里。

撰写回答