2016-09-05 108 views
1

我想弄清楚如何嵌入在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

謝謝!我已經改變了接受的答案和upvoted。我真的不喜歡這樣做,一旦我接受了其他答案,但你的將真的很方便我的目的 – BPL

+0

謝謝你的信任投票!希望參考課程能夠提供關於如何使用課程的充分例子。如果你想讓資源文件在被加載之後繼續存在,你可以在'load'方法中將'delete = True'更改爲'delete = False',或者你可以調用方法並傳遞'False'作爲其第二個論據。該類的一個創造性用途是嵌入您在程序中編寫的其他模塊,這些模塊是依賴關係所需的。 SMTP客戶端這樣做是爲了能夠在'tkinter'的類中加載一個線程安全的包裝器。總的來說,它工作得很好。 –

3

最好的辦法是將圖片轉換成一個python字符串,並將其放入一個名爲resources.py的文件中,然後簡單地解析它。

如果您正在尋找嵌入一個二進制文件內整個事情,然後你看像py2exeHere就是一個例子嵌入外部文件

在第一種情況下,你甚至可以用base64來的(de)代碼的圖片,這樣的事情:

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