28

我正在尝试在不保留.py源文件的情况下运行 Python 应用程序,而只依赖已.pyc编译的文件。但是,当我删除.py源文件时出现导入错误。此功能在 Python 2.7 中有效,但在 3.4(具有新__pycache__结构)中无效。

这是一个示例目录结构:

package/
  __init__.py
  module.py

蟒蛇 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

蟒蛇 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文件的情况下正确导入/执行?这是期望的行为,意味着在所有情况下都必须保留源吗?还是我错过了一些愚蠢的东西?

4

2 回答 2

35

没有足够的声誉来为 BrenBarn 的答案添加评论。所以这里有一些补充。

根据compileall文档:

-b

将字节码文件写入它们的旧位置和名称,这可能会覆盖由另一个版本的 Python 创建的字节码文件。默认是将文件写入其 PEP 3147 位置和名称,这允许来自多个 Python 版本的字节码文件共存。

因此,您可以运行python -m compileall -b .以递归方式编译该目录中的所有代码文件。

于 2015-09-14T03:56:53.083 回答
31

根据PEP

foo.py 文件可能以某种方式被删除,而缓存的 pyc 文件仍保留在文件系统上。如果__pycache__/foo.<magic>.pyc文件存在,但用于创建它的 foo.py 文件不存在,Python 将在被要求导入 foo 时引发 ImportError。换句话说,除非源文件存在,否则 Python 不会从缓存目录中导入 pyc 文件。

但:

但是,为了继续支持无源发行版,如果源文件丢失,Python 将导入一个单独的 pyc 文件(如果它位于源文件所在的位置)。

所以看起来__pycache__和无源发行版是相互排斥的。如果要删除源代码,则需要将 .pyc 文件移到源代码所在的目录中。

于 2014-08-07T01:33:11.953 回答