Python:可执行的zip文件是否可以包含数据文件?

24

作为一个相对新手的Python开发者,我最近才发现可以通过在文件顶部放置一个__main__.py文件来直接执行.zip文件。这对于Python代码来说非常好用,但是我能否打包其他类型的文件并在脚本中访问它们呢?如果可以,怎么做?

我的最终目标是将一些图像文件与Python代码捆绑在单个.zip文件中,然后能够在应用程序中使用这些图像而无需将它们提取到磁盘上。我还想捆绑版权声明、发布说明等内容,使整个应用程序及其数据文件都在一个单独的zip文件中,可以直接执行而不必在某处解压缩。

4个回答

21

您可以使用pkg_resources函数访问文件:

# __main__.py
import pkg_resources
from PIL import Image

print pkg_resources.resource_string(__name__, 'README.txt')

im = Image.open(pkg_resources.resource_stream('app', 'im.png'))
im.rotate(45).show()

zipfile 包含以下内容:

.
|-- app
|   |-- im.png
|   `-- __init__.py
|-- README.txt
`-- __main__.py

要使 zipfile 成为可执行文件,请运行:

$ echo '#!/usr/bin/env python' | cat - zipfile > program-name
$ chmod +x program-name

测试它:

$ cp program-name /another-dir/
$ cd /another-dir && ./program-name

1
这仅适用于安装了setuptools的pkg_resources模块。 - Xavier Combelle
2
Python 3.5 中有一个名为 zipapp 的模块,可以自动化地创建归档文件。 - jfs

6

至少在我的Linux系统上,该进程没有打开文件句柄或将内存映射到其自己的zip文件中,因此可能没有“神奇地”访问它的方法。

但是,创建自己的访问并不难。像这样创建一个__main__.py

import os, zipfile

me = zipfile.ZipFile(os.path.dirname(__file__), 'r')
f = me.open('other.txt')
print f.read()
f.close()
me.close()

编辑:有点简洁了。为了完整起见:

$ echo "Hello ZIP" > other.txt
$ zip testo.zip __main__.py other.txt
$ python testo.zip
Hello ZIP

我从未想过重新打开当前正在执行的zip文件。很酷。不幸的是,当我在OSX上运行我的脚本时,__file__None。不过我认为这是朝着正确方向迈出的一步。 - Bryan Oakley
@Bryan Oakley: 如果压缩文件的第一行是 #!/usr/bin/env python 或类似的内容,那么 sys.argv[0] 就包含了压缩文件的路径。 - jfs

6

只有当我把文件放在一个包中时,这才有效,这意味着README.txt或COPYRIGHT.txt文件无法在zip文件的根目录下工作。这不是关键问题,但我希望能够将一些文件放在__main__.py顶部的zip文件层次结构旁边。 - Bryan Oakley
通常情况下,您的主模块可以通过导入__main__来访问,您可以引用它。 - Xavier Combelle
Combell:我希望这是真的。至少在我的OSX和Python 2.6测试.zip文件中,__main__未定义。 - Bryan Oakley
@Bryan Oakley:zipfile作为sys.path中的第一项添加,因此您应该始终能够导入__main__。我已经测试了我的答案,并且存储在zip文件中与__main__.py并列的README.txt可以被很好地读取。https://dev59.com/52435IYBdhLWcg3wigqx#5356563 - jfs
s/I've test/I've tested/ ^^ - jfs

1

pkgutil.get_data(package, resource) 接受一个包的名称和资源。这意味着您必须将数据文件放在 zip 文件中的包内。

例如,一个包含以下内容的 zip 文件:

__main__.py
zippeddata/__init__.py
zippeddata/data.txt

__init__.py文件可以为空或只是一个注释,但你需要一个来使zippeddata可导入。

然后在__main__.py中,你只需调用:

data = pkgutil.get_data('zippeddata', 'data.txt')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接