【问题标题】:embedding resources in python scripts在 python 脚本中嵌入资源
【发布时间】:2016-09-05 20:58:45
【问题描述】:

我想弄清楚如何在 python 脚本中嵌入二进制内容。例如,我不希望有任何外部文件(图像、声音、...),我希望所有这些内容都存在于我的 python 脚本中。

小例子澄清一下,假设我有这个小sn-p:

from StringIO import StringIO
from PIL import Image, ImageFilter

embedded_resource = StringIO(open("Lenna.png", "rb").read())
im = Image.open(embedded_resource)
im.show()

im_sharp = im.filter(ImageFilter.SHARPEN)
im_sharp.show()

如您所见,该示例正在读取外部文件“Lenna.png”

问题

如何继续将“Lenna.png”作为资源(变量)嵌入到我的 python 脚本中。使用 python 完成这个简单任务的最快方法是什么?

【问题讨论】:

  • 我唯一能想到的就是将图像转换为“原始”数据并将其存储在变量中。不知道这是否非常pythonic

标签: python embedded-resource


【解决方案1】:

您可能会发现以下类对于在程序中嵌入资源非常有用。要使用它,请使用要嵌入的文件的路径调用 package 方法。该类将打印出DATA 属性,该属性应用于替换已在类中找到的属性。如果您想将文件添加到预建数据中,请改用add 方法。要在程序中使用该类,请使用上下文管理器语法调用 load 方法。返回值是一个Path 对象,可用作其他函数的文件名参数或用于直接加载重组文件的目的。请参阅此SMTP Client 示例用法。

import base64
import contextlib
import pathlib
import pickle
import pickletools
import sys
import zlib


class Resource:

    """Manager for resources that would normally be held externally."""

    WIDTH = 76
    __CACHE = None
    DATA = b''

    @classmethod
    def package(cls, *paths):
        """Creates a resource string to be copied into the class."""
        cls.__generate_data(paths, {})

    @classmethod
    def add(cls, *paths):
        """Include paths in the pre-generated DATA block up above."""
        cls.__preload()
        cls.__generate_data(paths, cls.__CACHE.copy())

    @classmethod
    def __generate_data(cls, paths, buffer):
        """Load paths into buffer and output DATA code for the class."""
        for path in map(pathlib.Path, paths):
            if not path.is_file():
                raise ValueError('{!r} is not a file'.format(path))
            key = path.name
            if key in buffer:
                raise KeyError('{!r} has already been included'.format(key))
            with path.open('rb') as file:
                buffer[key] = file.read()
        pickled = pickle.dumps(buffer, pickle.HIGHEST_PROTOCOL)
        optimized = pickletools.optimize(pickled)
        compressed = zlib.compress(optimized, zlib.Z_BEST_COMPRESSION)
        encoded = base64.b85encode(compressed)
        cls.__print("    DATA = b'''")
        for offset in range(0, len(encoded), cls.WIDTH):
            cls.__print("\\\n" + encoded[
                slice(offset, offset + cls.WIDTH)].decode('ascii'))
        cls.__print("'''")

    @staticmethod
    def __print(line):
        """Provides alternative printing interface for simplicity."""
        sys.stdout.write(line)
        sys.stdout.flush()

    @classmethod
    @contextlib.contextmanager
    def load(cls, name, delete=True):
        """Dynamically loads resources and makes them usable while needed."""
        cls.__preload()
        if name not in cls.__CACHE:
            raise KeyError('{!r} cannot be found'.format(name))
        path = pathlib.Path(name)
        with path.open('wb') as file:
            file.write(cls.__CACHE[name])
        yield path
        if delete:
            path.unlink()

    @classmethod
    def __preload(cls):
        """Warm up the cache if it does not exist in a ready state yet."""
        if cls.__CACHE is None:
            decoded = base64.b85decode(cls.DATA)
            decompressed = zlib.decompress(decoded)
            cls.__CACHE = pickle.loads(decompressed)

    def __init__(self):
        """Creates an error explaining class was used improperly."""
        raise NotImplementedError('class was not designed for instantiation')

【讨论】:

  • 谢谢!我已经改变了接受的答案并赞成。一旦我接受了另一个答案,我真的不喜欢这样做,但你的答案对我的目的来说真的很方便
  • 感谢您的信任投票!希望参考程序将提供有关如何使用该类的足够示例。如果您希望资源文件在加载后仍然存在,您可以在load 方法中将delete=True 更改为delete=False,或者您可以调用该方法并将False 作为其第二个参数传递。该类的一个创造性用途是嵌入您在程序中编写的其他模块,这些模块需要作为依赖项。 SMTP 客户端这样做是为了在tkinter 中的类周围加载一个线程安全的包装器。总体而言,它运作良好。
【解决方案2】:

解决此问题的最佳方法是将您的图片转换为 python 字符串,并将其放在一个名为 resources.py 之类的单独文件中,然后您只需对其进行解析。

如果您希望将整个内容嵌入到单个二进制文件中,那么您正在查看类似py2exe 的内容。 Here 是嵌入外部文件的示例

在第一种情况下,您甚至可以使用base64 对图片进行(解码)编码,如下所示:

import base64
file = open('yourImage.png'); 
encoded = base64.b64encode(file.read())
data = base64.b64decode(encoded) # Don't forget to file.close() !

【讨论】:

  • Av4t4r 我现在要发布base64 解决方案:)。是的,这就是我要找的,谢谢。
猜你喜欢
  • 2015-02-24
  • 2012-06-26
  • 1970-01-01
  • 2017-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
相关资源
最近更新 更多