在 python 个脚本中嵌入资源
embedding resources in python scripts
我想弄清楚如何在 python 脚本中嵌入二进制内容。例如,我不想有任何外部文件(图像、声音、...),我希望所有这些内容都存在于我的 python 脚本中。
澄清的小例子,假设我得到了这个小片段:
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 完成这个简单任务的最快方法是什么?
解决此问题的最佳方法是将您的图片转换为 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() !
您可能会发现以下 class 对于在您的程序中嵌入资源非常有用。要使用它,请使用要嵌入的文件的路径调用 package
方法。 class 将打印出 DATA
属性,该属性应用于替换 class 中已经找到的属性。如果要将文件添加到预建数据,请改用 add
方法。要在您的程序中使用 class,请使用上下文管理器语法调用 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')
我想弄清楚如何在 python 脚本中嵌入二进制内容。例如,我不想有任何外部文件(图像、声音、...),我希望所有这些内容都存在于我的 python 脚本中。
澄清的小例子,假设我得到了这个小片段:
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 完成这个简单任务的最快方法是什么?
解决此问题的最佳方法是将您的图片转换为 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() !
您可能会发现以下 class 对于在您的程序中嵌入资源非常有用。要使用它,请使用要嵌入的文件的路径调用 package
方法。 class 将打印出 DATA
属性,该属性应用于替换 class 中已经找到的属性。如果要将文件添加到预建数据,请改用 add
方法。要在您的程序中使用 class,请使用上下文管理器语法调用 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')