在Windows編程的時候,有些時候,我們經常會要使用一些非常規的方法,比如說從內存中加載DLL,然后使用DLL中的函數。於是就思索在用Python的時候是否能夠將幾個編譯好的Pyc合並成一個,然后使用動態的讀取這個文件,然后根據標記進行划分,獲得不同的模塊的pyc內容,然后動態作為一個新的模塊引用到我們的程序中去,這就涉及到一個問題,如何將一個pyc整到內存,然后從內存中獲取變成一個新模塊進行引入處理。之后找了一些資料之后,發現一種方法,就是通過PyCodeObject這個代碼對象來進行處理,也就是用compile編譯之后的內容,然后讀取形成codeobject對象,然后用types.ModuleType建立一個新的模塊,然后將這個新模塊加入到sys.modules字典中去,之后在這這個新建的模塊環境中執行前面讀取的codeObject對象,那么久可以使用這個新的導入模塊了,方式如下:
#代碼如下: PycContext = open('test.pyo', 'rb').read() import marshal #可以查看PyCodeObject數據結構前面8個位是一個4字節MagicNum和4字節的時間戳,所以從第八位開始 PyCodeObject = marshal.loads(b[8:]) import types #建立一個名字叫testSimple的新模塊 newModule = types.ModuleType('testSimple') import sys sys.modules['testSimple'] = newModule #這個時候已經可以用import testSimple了 #但是運行時候會發現,這個新模塊什么功能函數都沒有,因為還沒有和 #上面的PyCodeObject關聯起來,此時需要關聯 #就是使用exec在本模塊環境執行一次,則可 exec c in newModule.__dict__ #這樣,上面的codeObject和新模塊就關聯起來了,然后就可以使用里面的函數了
另外記錄一個外國的資料代碼如下
def load_compiled_from_memory(name, filename, data, ispackage=False): if data[:4]!=imp.get_magic(): raise ImportError('Bad magic number in %s' % filename) # Ignore timestamp in data[4:8] code = marshal.loads(data[8:]) imp.acquire_lock() # Required in threaded applications try: mod = imp.new_module(name) sys.modules[name] = mod # To handle circular and submodule imports # it should come before exec. try: mod.__file__ = filename # Is not so important. # For package you have to set mod.__path__ here. # Here I handle simple cases only. if ispackage: mod.__path__ = [name.replace('.', '/')] exec code in mod.__dict__ except: del sys.modules[name] raise finally: imp.release_lock() return mod
另外需要說明一下的是,使用Python import自動生成的編譯后的文件一般都帶有魔數和時間戳,也就是說讀取codeObject的時候需要移動8位,但是有些用Python的API生成的不一定帶有這個魔數和時間戳的,那么這個時候就不用移位,而直接讀取生成codeobject