在 Python 開發過程中,我們經常需要將不同類型的物件進行序列化,以便於存儲或通過網絡進行傳輸。對於基本數據類型和容器類型,這通常是一個直接的過程。然而,當涉及到更複雜的物件,如函數時,事情就變得有些棘手。

解法是利用 marshal 把 bytecode 轉成字串,就可以將之 Picklingmarshal 模組提供了一個用於序列化和反序列化 Python 物件的介面。這個模組主要用於 Python 的 .pyc 文件,即編譯過的 Python 源碼文件。marshal 模組的主要功能是將 Python 物件轉換為字串(序列化),以及將這些字串還原為 Python 物件(反序列化)。

Pickling

Python 中的 pickling 是一種序列化過程,它將任何 Python 物件轉換為字節流。這個過程通過 pickle 模組實現,允許物件被保存到文件或通過網絡傳輸,並且可以在稍後被完整地恢復或“unpickled”。這對於數據持久化、深度複製物件或在不同 Python 程序之間傳輸複雜數據結構非常有用。

  • 序列化(Pickling):將 Python 物件轉換為一個字節流的過程稱為 pickling。這通常是通過 pickle.dump() 函數實現的,該函數接受一個物件和一個文件物件作為參數,並將物件寫入文件。
  • 反序列化(Unpickling):將 pickled 的字節流轉換回原始 Python 物件的過程稱為 unpickling。這通常是通過 pickle.load() 函數實現的,該函數從文件中讀取 pickled 的字節流並將其轉換回物件。

把 Function 轉成 Pickled string

import pickle
import marshal
maintask_bytecode = marshal.dumps(maintask_callback.func_code)
output = {
  'maintask_bytecode': maintask_bytecode,
  'maintask_defaults': maintask_defaults,
  'maintask_args': maintask_args}
print pickle.dumps(output)

把Pickled string轉成function

import sys
import types
import marshal
import pickle
## \brief reconstruct function from bytecode
#  \param func_bytecode str marshaled bytecode of func_code
#  \param func_default dict func_defaults
#  \return Function
def reconstruct_function(func_bytecode, func_defaults):
    func_code = marshal.loads(func_bytecode)
    return types.FunctionType(func_code, globals(), func_code.co_name,
                             func_defaults)
string = sys.stdin.read()
received = pickle.loads(string)
callback = reconstruct_function(received['maintask_bytecode'],
      received['maintask_defaults'])
print callback(maintask_args)

這個技巧多用於要做語言互交互的情境,比如說在 Shell Script 跟 Python 之間互相調用對方的變數,函數。像是下面這段源碼, 就是讓 Shell Script 跟 Python 透過 Shell 的管道傳遞參數跟結果。

$ awk '{print $2}' /etc/apt/sources.list | grep http | \
  boliau-readstdin  | boliau-lines | \
  boliau-map --command "lambda e: e.count('u')" | \
  boliau-py-call sum | boliau-print
141

如果你對這很感興趣,可以閱讀我寫的boliau 的原始碼了解細節。