2016-09-05 9 views
1

Я хотел бы выяснить, как встраивать двоичный контент в скрипт python. Например, я не хочу иметь внешние файлы (изображения, звук, ...), я хочу, чтобы весь этот контент жил внутри моих сценариев 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'

enter image description here

Вопрос

Как перейти к внедрению «Lenna.png» в качестве ресурса (переменной) в мой скрипт python. Какой самый быстрый способ выполнить эту простую задачу с помощью python?

+0

Единственное, что я могу придумать, это преобразовать изображение в «сырые» данные и сохранить его в переменной. Не уверен, что это очень pythonic, хотя – UnholySheep

ответ

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') 
+0

Спасибо! Я изменил принятый ответ и поддержал его. Я не очень люблю это делать, как только я принял другой ответ, но ваш будет очень полезен для моих целей. – BPL

+0

Благодарим за доверие! Надеемся, что эталонная программа предоставит достаточный пример того, как использовать класс. Если вы хотите, чтобы файлы ресурсов задерживались после их загрузки, вы можете изменить 'delete = True' на' delete = False' в методе 'load' или вы можете вызвать метод и передать' False' в качестве второго аргумент. Одним из изобретательских применений этого класса является внедрение других модулей, которые вы написали в своей программе, которые необходимы в качестве зависимостей. Клиент SMTP делает это так, что он может загружать потокобезопасную оболочку вокруг классов в 'tkinter'. В целом, он работал довольно хорошо. –

3

Лучший способ сделать это - преобразовать изображение в строку 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() ! 
+1

Av4t4r Я собирался опубликовать решение [base64] (http://kb.worldviz.com/articles/878) прямо сейчас :). Да, это то, что я искал, спасибо. – BPL

 Смежные вопросы

  • Нет связанных вопросов^_^