Почему multiprocessing.Process может обрабатывать декорированные функции?

Итак, я прочитал здесь, что декорированные функции нельзя замариновать. Верно:

import multiprocessing as mp

def deco(f):
    def wrapper(*args, **kwargs):
        try:
            f(*args, **kwargs)
        except:
            print 'Exception caught!'
    return wrapper

@deco
def f(x):
    print x
    raise OverflowError 

if __name__ == '__main__':
    pool = mp.Pool(processes=1)
    for _ in pool.imap_unordered(f, range(10)):
        pass
    pool.close()
    pool.join()
    print 'All done'

Вне:

Traceback (most recent call last):
  File "deco0.py", line 19, in <module>
    for _ in pool.imap_unordered(f, range(10)):
  File "/Users/usualme/anaconda/lib/python2.7/multiprocessing/pool.py", line 659, in next
    raise value
cPickle.PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

Но теперь, если я заменю map на Process:

import multiprocessing as mp

def deco(f):
    def wrapper(*args, **kwargs):
        try:
            f(*args, **kwargs)
        except:
            print 'Exception caught!'
    return wrapper

@deco
def f(x):
    print x
    raise OverflowError 

if __name__ == '__main__':
    p = mp.Process(target=f, args=(1,))
    p.start()
    p.join()
    print 'All done'

Вне:

1
Exception caught!
All done

Почему это работает? Разве процессу не нужно также травить декорированную функцию?


person usual me    schedule 09.10.2014    source источник
comment
multiprocessing использует cPickle в python 2.x и pickle в 3.x. Существует ответвление multiprocessing, использующее сериализатор dill... который может обрабатывать оформленные функции. Форк, названный pathos.multiprocessing, заменяет только сериализатор… и, таким образом, может принимать оформленные функции как в Pool, так и в Process, и работает как в Windows, так и в Linux, а также работает в интерпретаторе, где Pool или Process даже если не вызывается из __main__. См.: github.com/uqfoundation.   -  person Mike McKerns    schedule 09.10.2014


Ответы (1)


Это работает, потому что вы работаете в Linux, которому не нужно мариновать f, чтобы вызвать его в дочернем процессе через Process.__init__. Это потому, что f наследуется потомком через os.fork. Если вы запустите один и тот же код в Windows (в котором отсутствует fork) или попытаетесь передать f в Pool.apply/Pool/map (в обоих случаях потребуется распараллелить f, чтобы вызвать его в подпроцессе), вы получите сообщение об ошибке.

Этот пример не будет работать независимо от того, какую платформу вы используете:

import multiprocessing as mp

def deco(f):
    def wrapper(*args, **kwargs):
        try:
            f(*args, **kwargs)
        except:
            print 'Exception caught!'
    return wrapper

@deco
def f(x):
    print x
    raise OverflowError 

if __name__ == '__main__':
    p = mp.Pool()
    p.apply(f, args=(1,))  # f needs to be pickled here.
    print 'All done'

Вывод:

1
Exception caught!
Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 504, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib/python2.7/multiprocessing/pool.py", line 319, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
person dano    schedule 09.10.2014