2012-04-06 64 views
11

作爲後續到這樣一個問題: Is there an easy way to pickle a python function (or otherwise serialize its code)?如何醃一個python函數及其依賴項?

我想看到從上述交此子彈的一個示例:

「如果函數引用全局(包括導入模塊,其他功能等)你需要提取它們,或者在遠程端重新創建它們,我的示例只是給它提供了遠程進程的全局名稱空間。「

我有一個簡單的測試去的地方我使用元帥寫一個函數字節碼文件:

def g(self,blah): 
    print blah 

def f(self): 
    for i in range(1,5): 
     print 'some function f' 
     g('some string used by g') 

data = marshal.dumps(f.func_code) 

file = open('/tmp/f2.txt', 'w') 
file.write(data) 

然後開始一個新的蟒蛇比如我做的事:

file = open('/tmp/f2.txt', 'r') 
code = marshal.loads(file.read()) 
func2 = types.FunctionType(code, globals(), "some_func_name"); 
func2('blah') 

這導致a:

NameError: global name 'g' is not defined 

這與我對包括g的不同方法無關。我已經嘗試了基本上相同的方法來發送g作爲f,但f仍然看不到g。我如何讓g進入全局命名空間,以便f可以在接收過程中使用它?

有人還建議以pyro爲例說明如何做到這一點。我已經嘗試瞭解迪斯科項目中的相關代碼。我帶着他們的dPickle類,並嘗試在獨立的應用程序中重新創建他們的disco/tests/test_pickle.py功能,但沒有成功。我的實驗在調用dump函數時遇到了問題。無論如何,也許下一個pyro探險。

總的來說,我所能達到的基本功能是能夠通過電線發送一種方法,並將所有基本的「工作空間」方法(如g)發送出去。

實施例與來自答案改變:

工作function_writer:

import marshal, types 

def g(blah): 
    print blah 


def f(): 
    for i in range(1,5): 
     print 'some function f' 
     g('blah string used by g') 


f_data = marshal.dumps(f.func_code) 
g_data = marshal.dumps(g.func_code); 

f_file = open('/tmp/f.txt', 'w') 
f_file.write(f_data) 

g_file = open('/tmp/g.txt', 'w') 
g_file.write(g_data) 

工作function_reader:

import marshal, types 

f_file = open('/tmp/f.txt', 'r') 
g_file = open('/tmp/g.txt', 'r') 

f_code = marshal.loads(f_file.read()) 
g_code = marshal.loads(g_file.read()) 

f = types.FunctionType(f_code, globals(), 'f'); 
g = types.FunctionType(g_code, globals(), 'g'); 

f() 

回答

3

我已經基本試圖同樣的方法在爲f但是在發送克f仍然看不到g。我如何讓g進入全局命名空間,以便f可以在接收過程中使用它?

將其指定爲全局名g。 (我看到你正在分配ffunc2而不是f如果你正在使用g這樣做,那麼很明顯爲什麼f找不到g。請記住,名稱解析發生在運行時 - g未被查看直到你致電f。)

當然,我猜你沒有顯示你用來做這件事的代碼。

最好創建一個單獨的字典,用於全局名稱空間,用於取消選中的函數 - 沙箱。這樣,他們所有的全局變量將與您正在執行的模塊分開。所以,你可能做這樣的事情:

sandbox = {} 

with open("functions.pickle", "rb") as funcfile: 
    while True: 
     try: 
      code = marshal.load(funcfile) 
     except EOFError: 
      break 
     sandbox[code.co_name] = types.FunctionType(code, sandbox, code.co_name) 

在這個例子中,我假設你已經把所有的功能代碼的物體在一個文件中,一個接一個,並在閱讀它們時,我得到的代碼對象的名稱,並將其用作函數對象的名稱和存儲在沙箱字典中的名稱的基礎。

在未撥號的函數中,沙盒字典是globals(),所以在f()裏,g的值從sandbox["g"]得到。要撥打f,那麼將會是:sandbox["f"]("blah")

+0

哇哦,我不知道指定的參考了一定的作用!謝謝!將發佈工作代碼。 – 2012-04-06 19:36:47

+1

@RyanR。 :自己發佈一些代碼。 – kindall 2012-04-06 19:41:03

+0

太棒了,我喜歡沙箱。想要自動探索下一個自動序列化所有函數依賴項。有點像迪斯科modutil.find_modules方法所做的。感謝幫助。 – 2012-04-06 20:01:36

2

每個模塊都有其自己的全局變量,沒有通用全局變量。我們可以將恢復的功能「植入」到某個模塊中,並像普通模塊一樣使用它。

- 保存 -

import marshal 
def f(x): 
    return x + 1 
def g(x): 
    return f(x) ** 2 
funcfile = open("functions.pickle", "wb") 
marshal.dump(f.func_code, funcfile) 
marshal.dump(g.func_code, funcfile) 
funcfile.close() 

- 恢復 -

import marshal 
import types 
open('sandbox.py', 'w').write('') # create an empty module 'sandbox' 
import sandbox 
with open("functions.pickle", "rb") as funcfile: 
    while True: 
     try: 
      code = marshal.load(funcfile) 
     except EOFError: 
      break 
     func = types.FunctionType(code, sandbox.__dict__, code.co_name) 
     setattr(sandbox, code.co_name, func) # or sandbox.f = ... if the name is fixed 
assert sandbox.g(3) == 16 # f(3) ** 2 
# it is possible import them from other modules 
from sandbox import g 

編輯:
你可以做也進口一些模塊.e.g。 「SYS」來自外部的「沙箱」的命名空間:

sandbox.sys = __import__('sys') 

或相同:

exec 'import sys' in sandbox.__dict__ 
assert 'sys' in sandbox, 'Verify imported into sandbox' 

你的原始代碼將工作,如果你在IPython中的互動,但在Python程序或做不正常的python互動!

Ipython使用一些奇怪的名稱空間,它不是sys.modules中任何模塊的字典。普通Python或任何主程序使用全局變量()作爲sys.modules['__main__'].__dict__。任何模塊使用that_module.__dict__這也沒關係,只有ipython交互是一個問題。

+0

謝謝! +1也很好奇。 – 2012-04-07 00:42:44

+1

@RyanR。如果使用普通的python而不是ipython,你的原始代碼將工作。 – hynekcer 2012-04-07 09:31:18

+0

是不是'import x; x.method()'類型在遠程​​腳本中使用了一個問題?如:HTTP://stackoverflow.com/questions/10099326/how-to-do-an-embedded-python-module-for-remote-sandbox-execution – 2012-04-11 03:42:38

16

雲包做到這一點 - 只是「PIP雲安裝」,然後:

import cloud, pickle 
def foo(x): 
    return x*3 
def bar(z): 
    return foo(z)+1 
x = cloud.serialization.cloudpickle.dumps(bar) 
del foo 
del bar 
f = pickle.loads(x) 
print f(3) # displays "10" 

換句話說,只需要調用cloudpickle.dump()或cloudpickle.dumps(),你會以同樣的方式使用pickle。*,然後使用本地pickle.load()或pickle.loads()解凍。

Picloud在LGPL下發布了'雲'python包,其他開源項目已經在使用它(谷歌爲「cloudpickle.py」來看幾個)。 picloud.com上的文檔讓你瞭解這個代碼有多強大,以及爲什麼他們有動力去做通用代碼酸洗工作 - 他們的整個業務都是圍繞它建立的。我們的想法是,如果你有cpu_intensive_function(),並希望能夠在亞馬遜的EC2電網運行它,你只需更換:

cpu_intensive_function(some, args) 

有:

cloud.call(cpu_intensive_function, some, args) 

後者使用cloudpickle鹹菜的任何相關代碼和數據,將其發送到EC2,運行它,並在調用cloud.result()時將結果返回給您。(Picloud賬單以毫秒爲增量,它的價格很便宜,我總是用它來進行蒙特卡洛模擬和財務時間序列分析,當我需要幾百個CPU核心時,每個核心只需幾秒鐘。關於它的事情,我甚至沒有在那裏工作。)

+0

謝謝主席先生:) 我一直在掙扎與蒔蘿幾個小時,但云只是直接前進 我相信這應該是公認的答案 – NiCU 2014-03-13 08:34:14

+2

由於原來的PiCloud客戶端SDK不再維護,一個新的項目開始只是爲了維護雲端功能:http:// github.com/cloudpipe/cloudpickle:'PIP安裝cloudpickle' – ogrisel 2015-04-30 08:33:41

+1

@stevegt:你的例子似乎並不但隨後如果一個Python程序定義foo和bar,鹹菜扎入一個文件(使用保存在內置功能正常:) – user2284570 2016-08-20 00:38:08

3

通過導入__main__並使用該模塊中可用的方法,可以更好地處理全局對象。這是dill爲了序列化python中的幾乎任何東西。基本上,當蒔蘿序列化的交互定義的函數,它使用了一些名稱上的串行化和反串行化側,使__main__有效模塊上都__main__重整。

>>> import dill 
>>> 
>>> def bar(x): 
... return foo(x) + x 
... 
>>> def foo(x): 
... return x**2 
... 
>>> bar(3) 
12 
>>> 
>>> _bar = dill.loads(dill.dumps(bar)) 
>>> _bar(3) 
12 

其實,蒔蘿註冊它的類型分爲pickle註冊表,所以如果你有一個使用pickle一些黑盒代碼,你不能編輯它,然後只導入蒔蘿能神奇地度過,即使沒有的Monkeypatching工作第三方代碼。

或者,如果你想送過來爲「蟒蛇形象」整個解釋器會話,蒔蘿也可以這樣做。

>>> # continuing from above 
>>> dill.dump_session('foobar.pkl') 
>>> 
>>> ^D 
[email protected]>$ python 
Python 2.7.5 (default, Sep 30 2013, 20:15:49) 
[GCC 4.2.1 (Apple Inc. build 5566)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import dill 
>>> dill.load_session('foobar.pkl') 
>>> _bar(3) 
12 

您可以輕鬆地在SSH發送圖像到另一臺計算機,並開始在那裏你離開那裏,只要有泡菜的版本兼容性和有關Python改變和正在安裝的東西平時的告誡。

+0

蒔蘿),另一個Python程序將醃製文件加載到_bar中並調用_bar(3),它在foo未定義時出錯。爲什麼在這種情況下不起作用? – 2016-07-13 14:03:32

+0

我不確定我看到你究竟在問什麼,你能提供更多的細節(無論是在它自己的問題還是在gillub問題頁面上的'dill')? – 2016-07-13 21:40:41

+0

我在這裏打開一個新問題:https://github.com/uqfoundation/dill/issues/176 – 2016-07-13 22:01:02

0

蒔蘿(與其他泡菜變體,cloudpickle等一起)似乎工作酸洗的(多個)功能時的主要模塊與酸洗一起。如果您正在從另一個模塊酸洗一個功能,那麼當發生拆卸時,該模塊名稱必須存在。我似乎無法找到解決此限制的方法。

+0

你可以。參見:http://stackoverflow.com/questions/26389981/serialize-a-python-function-with-dependencies – 2014-10-15 22:34:34