2015-03-03 38 views
3

我已經研究過這個問題多次,但還沒有找到一種解決方法,或者適用於我的情況,或者我理解的方法,請耐心等待。如何解決python多處理的酸洗錯誤而不在頂層?

基本上,我有一個層次結構的函數,這是阻止我在頂層進行多處理。不幸的是,我不相信我可以改變程序的佈局 - 因爲我需要在初始輸入之後創建的所有變量。

例如,說我有這樣的:

import multiprocessing 

    def calculate(x): 
    # here is where I would take this input x (and maybe a couple more inputs) 
    # and build a larger library of variables that I use further down the line 

    def domath(y): 
     return x * y 

    pool = multiprocessing.Pool(3) 
    final= pool.map(domath, range(3)) 

calculate(2) 

我們得到以下錯誤:

Can't pickle <type 'function'>: attribute lookup __builtin__.function failed 

我想全局的,但恐怕我不得不定義太多,這可能會使我的程序減慢很多。 是否有任何解決方法,而不必重構整個程序?

+0

您可能需要閱讀試圖解決[相當多的時候我選擇的答案相同的錯誤](http://stackoverflow.com/questions/6234586/we-need-to-pickle-any-sort-of-callable)。 – motoku 2015-03-03 00:36:13

+0

Hey Sean,仔細看過你的問題後,恐怕我的解決方案可能會超出我的深度。無論如何,您可以給我一個關於這些函數在打包和解包時做什麼的更概念性概要? – Tim 2015-03-03 00:46:29

+0

當然。讓我一起看一些示例代碼。 – motoku 2015-03-03 00:47:58

回答

2

您可以使用pathos.multiprocessing,這是multiprocessing的分支,它使用dill序列化器而不是pickledill可以在python中序列化幾乎任何東西。然後,不需要編輯你的代碼。

>>> from pathos.multiprocessing import ProcessingPool as Pool 
>>> 
>>> def calculate(x): 
... def domath(y): 
...  return x*y 
... return Pool().map(domath, range(3)) 
... 
>>> calculate(2) 
[0, 2, 4] 

你甚至可以堅持下去......因爲大部分東西都被醃製了。沒有必要用純粹的multiprocessing來製作奇怪的非pythonic解決方案。

>>> class Foo(object): 
... def __init__(self, x): 
...  self.x = x 
... def doit(self, y): 
...  return ProcessingPool().map(self.squared, calculate(y+self.x)) 
... def squared(self, z): 
...  return z*z 
... 
>>> def thing(obj, y): 
... return getattr(obj, 'doit')(y) 
... 
>>> ProcessingPool().map(thing, ProcessingPool().map(Foo, range(3)), range(3)) 
[[0, 0, 0], [0, 4, 16], [0, 16, 64]] 

獲取pathos這裏:https://github.com/uqfoundation

+0

謝謝邁克!這很好。關於http://trac.mystic.cacr.caltech.edu/project/pathos/wiki/Installation 上可用的文件的快速說明此版本(至少在我下載它時)沒有包含多處理模塊。我認爲這可能是爲什麼一對夫婦用戶遇到了通過病態進行多處理的問題。在我從github而不是pathos頁面下載文件後,我擁有了我需要的一切。 – Tim 2015-03-03 18:36:15

+0

很酷。是的,我知道..謝謝你的提醒。您提到的'pathos' wiki鏈接上的版本非常陳舊(時間戳:06/28/10 17:50)。 github代碼是最新的,新版本是「即將推出」的。當時我會用新的穩定版本更新所有鏈接。 – 2015-03-03 20:31:37

2

您遇到的問題實際上是一項功能。醃菜源實際上是爲防止這種行爲而設計的,以防止惡意代碼被執行。在處理任何適用的安全實施時請考慮這一點。

首先我們有一些進口。

import marshal 
import pickle 
import types 

在這裏,我們有一個函數,它接受在一個函數作爲參數,鹹菜對象的部分,然後返回一個包含所有的部分的元組:

def pack(fn): 
    code = marshal.dumps(fn.__code__) 
    name = pickle.dumps(fn.__name__) 
    defs = pickle.dumps(fn.__defaults__) 
    clos = pickle.dumps(fn.__closure__) 
    return (code, name, defs, clos) 

接下來,我們有一個函數,接受我們轉換函數的四個部分。它翻譯這四個部分,然後創建並從這些部分返回一個函數。你應該注意到全局變量被重新引入到這裏,因爲我們的過程並不處理這些變量:

def unpack(code, name, defs, clos): 
    code = marshal.loads(code) 
    glob = globals() 
    name = pickle.loads(name) 
    defs = pickle.loads(defs) 
    clos = pickle.loads(clos) 
    return types.FunctionType(code, glob, name, defs, clos) 

這裏我們有一個測試函數。注意我在函數的範圍內放置了一個導入。全局不通過我們的酸洗工藝處理:

def test_function(a, b): 
    from random import randint 
    return randint(a, b) 

最後,我們收拾我們的測試對象,並打印出結果,以確保一切工作:

packed = pack(test_function) 
print((packed)) 

最後,我們解開我們的功能,把它分配給一個變量,打電話給它,並打印它的輸出:

unpacked = unpack(*packed) 
print((unpacked(2, 20))) 

如果您有任何問題,請留言。

+0

感謝您抽出寶貴時間詳細解釋!我試圖將此應用於我上面發佈的示例,但我仍遇到問題。當它解包時,它是否不返回與之前相同的格式化函數?當我運行這個方法時,我仍然遇到酸洗錯誤(這可能意味着我錯誤地使用了它)。 編輯:你怎麼看@mike答案? – Tim 2015-03-03 05:51:56

+0

@Tim是的,「解包」功能應該與原來的一樣。如果您想存儲原始源代碼,則需要改爲處理字符串。至於Mike的回答,我認爲這可能是一個更好的選擇。 – motoku 2015-03-03 15:29:44

+0

@SeanPedersen:這實際上是'dill'爲你做的,除了它創建了與存儲相同的可調用類型...所以如果你醃一個lambda,你得到一個lambda,或者如果你醃製一個綁定方法,你會得到一個綁定方法返回,等等。在大多數情況下,它也可以處理全局變量,並且還可以像處理模塊一樣處理'__main__'。 – 2015-03-03 17:59:48

0

怎麼樣只是把嵌入式功能了呢?

這在我看來,最明顯的解決方案(因爲你沒有給你預期的輸出,我猜):

$ cat /tmp/tmp.py 
import multiprocessing 

def calculate(x): 
    # here is where I would take this input x (and maybe a couple more inputs) 
    # and build a larger library of variables that I use further down the line 

    pool = multiprocessing.Pool(3) 
    _lst = [(x, y) for x in (x,) for y in range(3)] 
    final= pool.map(domath, _lst) 
    print(final) 

def domath(l): 
    return l[0] * l[1] 

calculate(2) 

$ python /tmp/tmp.py 
[0, 2, 4] 

$