2013-04-10 208 views
1

我最近看到一個關於如何functions are objects in python的答案/評論。所以,我想知道爲什麼當我採用這個例子,並且在初始化一個變量時創建一個類時,它不會以相同的方式工作。 (該類示例收到酸洗錯誤): PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin__.instancemethod failed函數初始化和對象初始化(多處理)

有沒有人知道這是爲什麼?從鏈路

示例代碼:

import multiprocessing as mp 

def f(x): 
    f.q.put('Doing: ' + str(x)) 
    return x*x 

def f_init(q): 
    f.q = q 

def main(): 
    jobs = range(1,6) 

    q = mp.Queue() 
    p = mp.Pool(None, f_init, [q]) 
    results = p.imap(f, jobs) 
    p.close() 

    for i in range(len(jobs)): 
     print q.get() 
     print results.next() 

if __name__ == '__main__': 
    main() 

相同示例而puttin f成類:

import multiprocessing as mp 

class F: 
    def __init__(self, q): 
     self.q = q 
    def f(x): 
     self.q.put('Doing: ' + str(x)) 
     return x*x 

def main(): 
    jobs = range(1,6) 

    q = mp.Queue() 
    p = mp.Pool(None) 
    f = F(q) 
    results = p.imap(f.f, jobs) 
    p.close() 

    for i in range(len(jobs)): 
     print q.get() 
     print results.next() 

if __name__ == '__main__': 
    main() 
+0

,如果你用什麼樣的第二段具有不同的行爲表示這將是有益的。 – cdhowie 2013-04-10 17:33:22

+0

@cdhowie,感謝您的輸入,我添加了一些信息。 – chase 2013-04-10 17:46:06

回答

3

實例方法不會自動picklable。因此

p.imap(f.f, jobs) 

失敗,因爲p.imap試圖醃製參數。 有「教」泡菜how to pickle instance methods(見史蒂芬Bethard的答案), 的方式,但你的代碼有一個問題:傳遞的隊列實例導致拋出一個RuntimeError:

RuntimeError: Queue objects should only be shared between processes through inheritance 

該錯誤消息是一點點因爲你可以通過隊列作爲參數p.imap,但你不能先將它傳遞給類F,然後通過f.f將它傳遞給工作進程,這讓我感到困惑(至少對我來說)。

無論如何,由於這些問題,我建議堅持使用原始代碼而不是試圖將代碼包裝到類中。


下面是如何來醃製實例方法的例子:

import multiprocessing as mp 
import copy_reg 
import types 

def _pickle_method(method): 
    # Author: Steven Bethard (author of argparse) 
    # http://bytes.com/topic/python/answers/552476-why-cant-you-pickle- 
    # instancemethods 
    func_name = method.im_func.__name__ 
    obj = method.im_self 
    cls = method.im_class 
    cls_name = '' 
    if func_name.startswith('__') and not func_name.endswith('__'): 
     cls_name = cls.__name__.lstrip('_') 
    if cls_name: 
     func_name = '_' + cls_name + func_name 
    return _unpickle_method, (func_name, obj, cls) 


def _unpickle_method(func_name, obj, cls): 
    # Author: Steven Bethard 
    # http://bytes.com/topic/python/answers/552476-why-cant-you-pickle- 
    # instancemethods 
    for cls in cls.mro(): 
     try: 
      func = cls.__dict__[func_name] 
     except KeyError: 
      pass 
     else: 
      break 
    return func.__get__(obj, cls) 

# This call to copy_reg.pickle allows you to pass methods as the first arg to 
# mp.Pool methods. If you comment out this line, `pool.map(self.foo, ...)` results in 
# PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup 
# __builtin__.instancemethod failed 

copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method) 

class F(object): 
    def f(self, x): 
     fq.put('Doing: ' + str(x))   
     return x*x 

def f_init(q): 
    # http://stackoverflow.com/a/3843313/190597 (Olson) 
    global fq 
    fq = q 

def main(): 
    jobs = range(1,6) 
    q = mp.Queue() 
    p = mp.Pool(None, f_init, [q]) 
    f = F() 
    results = p.imap(f.f, jobs) 
    p.close() 

    for r in results: 
     print(r, q.get()) 

if __name__ == '__main__': 
    main() 

產生

(1, 'Doing: 2') 
(4, 'Doing: 3') 
(9, 'Doing: 4') 
(16, 'Doing: 1') 
(25, 'Doing: 5') 
+0

感謝您的回答!那麼這是否意味着每個*'multiprocessing.Process()'(或'Pool()','imap()'等)***都是全局函數,而不是一個對象? – chase 2013-04-10 17:43:35

+0

不完全。 'mp.Process()'或'Pool()'可以在本地作用域中定義,但**參數**到'Process'或'imap'等必須是**可選**。這裏是[可自動選擇的東西]的列表(http://docs.python.org/2/library/pickle.html#what-c​​an-be-pickled-and-unpickled)。 – unutbu 2013-04-10 17:47:43

+0

好的,謝謝@unutbu!我只是想知道這一切是如何工作的,因爲我認爲對於寫入隊列以更新GUI的不同對象的函數具有多個進程是非常期望和常見的。 我知道在一個隊列中傳遞的對象必須是可選擇的,但是我沒有意識到如果object.function發送的東西沒有隊列寫入,你怎麼能通過隊列傳遞東西。 – chase 2013-04-10 17:57:26