Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cassandra/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
具有多处理的Python装饰器失败_Python_Decorator_Multiprocessing - Fatal编程技术网

具有多处理的Python装饰器失败

具有多处理的Python装饰器失败,python,decorator,multiprocessing,Python,Decorator,Multiprocessing,我想在一个函数上使用一个装饰器,然后将其传递给一个多处理池。但是,代码失败,出现“PicklingError:cannotpickle:attributelookup\uuuu builtin\uuuu.function failed”。我不太明白为什么它在这里失败了。我确信这很简单,但我找不到。下面是一个最小的“工作”示例。我认为使用functools函数就足以让它工作 如果我把功能装饰注释掉,它就可以正常工作了。我在这里误解的是关于多重处理的什么?有什么办法可以让这一切顺利进行吗 编辑:在添

我想在一个函数上使用一个装饰器,然后将其传递给一个多处理池。但是,代码失败,出现“PicklingError:cannotpickle:attributelookup
\uuuu builtin\uuuu
.function failed”。我不太明白为什么它在这里失败了。我确信这很简单,但我找不到。下面是一个最小的“工作”示例。我认为使用
functools
函数就足以让它工作

如果我把功能装饰注释掉,它就可以正常工作了。我在这里误解的是关于多重处理的什么?有什么办法可以让这一切顺利进行吗

编辑:在添加了可调用的类装饰器和函数装饰器之后,结果证明函数装饰器可以按预期工作。可调用类装饰程序继续失败。什么是可调用类版本使其不被pickle

import random
import multiprocessing
import functools

class my_decorator_class(object):
    def __init__(self, target):
        self.target = target
        try:
            functools.update_wrapper(self, target)
        except:
            pass

    def __call__(self, elements):
        f = []
        for element in elements:
            f.append(self.target([element])[0])
        return f

def my_decorator_function(target):
    @functools.wraps(target)
    def inner(elements):
        f = []
        for element in elements:
            f.append(target([element])[0])
        return f
    return inner

@my_decorator_function
def my_func(elements):
    f = []
    for element in elements:
        f.append(sum(element))
    return f

if __name__ == '__main__':
    elements = [[random.randint(0, 9) for _ in range(5)] for _ in range(10)]
    pool = multiprocessing.Pool(processes=4)
    results = [pool.apply_async(my_func, ([e],)) for e in elements]
    pool.close()
    f = [r.get()[0] for r in results]
    print(f)

问题是pickle需要有一些方法来重新组装你所pickle的所有东西。请参见此处,查看可腌制的内容列表:

酸洗
my_func
时,需要对以下部件进行酸洗:

  • my\u decorator\u类的一个实例,名为
    my\u func

    这很好。Pickle将存储类的名称并Pickle其
    \uuuuu dict\uuuu
    内容。取消勾选时,它使用名称查找类,然后创建实例并填写
    \uu dict\uu
    内容。但是,
    \uuuuuu dict\uuuuu
    内容存在一个问题

  • 存储在
    myfunc.target
    中的原始
    myfunc
    的实例

    这不太好。这是一个顶级函数,通常可以对其进行酸洗。Pickle将存储函数的名称。然而,问题是名称“my_func”不再绑定到未修饰的函数,而是绑定到修饰的函数。这意味着pickle将无法查找未修饰的函数来重新创建对象。遗憾的是,pickle无法知道它试图pickle的对象总是可以在名称
    \uuuu main\uuuu.my\u func
    下找到

您可以这样更改它,它将工作:

import random
import multiprocessing
import functools

class my_decorator(object):
    def __init__(self, target):
        self.target = target
        try:
            functools.update_wrapper(self, target)
        except:
            pass

    def __call__(self, candidates, args):
        f = []
        for candidate in candidates:
            f.append(self.target([candidate], args)[0])
        return f

def old_my_func(candidates, args):
    f = []
    for c in candidates:
        f.append(sum(c))
    return f

my_func = my_decorator(old_my_func)

if __name__ == '__main__':
    candidates = [[random.randint(0, 9) for _ in range(5)] for _ in range(10)]
    pool = multiprocessing.Pool(processes=4)
    results = [pool.apply_async(my_func, ([c], {})) for c in candidates]
    pool.close()
    f = [r.get()[0] for r in results]
    print(f)


您已经注意到,当类不工作时,decorator函数可以工作。我认为这是因为
functools.wrapps
修改了修饰函数,使其具有所包装函数的名称和其他属性。就pickle模块所知,它与普通的顶级函数无法区分,因此它通过存储其名称来pickle它。取消勾选后,名称将绑定到修饰函数,以便一切正常。

在多处理中使用修饰符时,我也遇到了一些问题。我不确定是否与您的问题相同:

我的代码如下所示:

from multiprocessing import Pool

def decorate_func(f):
    def _decorate_func(*args, **kwargs):
        print "I'm decorating"
        return f(*args, **kwargs)
    return _decorate_func

@decorate_func
def actual_func(x):
    return x ** 2

my_swimming_pool = Pool()
result = my_swimming_pool.apply_async(actual_func,(2,))
print result.get()
@parallel(4)
def hello(args):
    #§ from time import sleep     # use '#§' to avoid unnecessary (re)imports in main program
    name, seconds = tuple(args)   # unpack args-list here
    sleep(seconds)
    print('Hi', name)
hello([['Marty', 0.5],
       ['Catherine', 0.9],
       ['Tyler', 0.7],
       ['Pavel', 0.3]])
当我运行代码时,我得到以下信息:

Traceback (most recent call last):
  File "test.py", line 15, in <module>
    print result.get()
  File "somedirectory_too_lengthy_to_put_here/lib/python2.7/multiprocessing/pool.py", line 572, in get
    raise self._value
cPickle.PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
代码运行良好,我得到:

I'm decorating
4

我在Python方面不是很有经验,但是这个解决方案为我解决了问题

如果你太想要装饰程序(像我一样),你也可以在函数字符串上使用
exec()
命令,以避免前面提到的酸洗

我希望能够将所有参数传递给原始函数,然后依次使用它们。下面是我的代码

首先,我创建了一个
make_functext()
函数来将目标函数对象转换为字符串。为此,我使用了
inspect
模块中的
getsource()
函数(请参阅documentation,注意它无法从编译后的代码中检索源代码等)。这是:

from inspect import getsource

def make_functext(func):
    ft = '\n'.join(getsource(func).split('\n')[1:]) # Removing the decorator, of course
    ft = ft.replace(func.__name__, 'func')          # Making function callable with 'func'
    ft = ft.replace('#§ ', '').replace('#§', '')    # For using commented code starting with '#§'
    ft = ft.strip()                                 # In case the function code was indented
    return ft
它用于以下将成为进程目标的
\u worker()
函数中:

def _worker(functext, args):
    scope = {}               # This is needed to keep executed definitions
    exec(functext, scope)
    scope['func'](args)      # Using func from scope
最后,这是我的装饰师:

from multiprocessing import Process 

def parallel(num_processes, **kwargs):
    def parallel_decorator(func, num_processes=num_processes):
        functext = make_functext(func)
        print('This is the parallelized function:\n', functext)
        def function_wrapper(funcargs, num_processes=num_processes):
            workers = []
            print('Launching processes...')
            for k in range(num_processes):
                p = Process(target=_worker, args=(functext, funcargs[k])) # use args here
                p.start()
                workers.append(p)
        return function_wrapper
    return parallel_decorator
代码最终可以通过定义如下函数来使用:

from multiprocessing import Pool

def decorate_func(f):
    def _decorate_func(*args, **kwargs):
        print "I'm decorating"
        return f(*args, **kwargs)
    return _decorate_func

@decorate_func
def actual_func(x):
    return x ** 2

my_swimming_pool = Pool()
result = my_swimming_pool.apply_async(actual_func,(2,))
print result.get()
@parallel(4)
def hello(args):
    #§ from time import sleep     # use '#§' to avoid unnecessary (re)imports in main program
    name, seconds = tuple(args)   # unpack args-list here
    sleep(seconds)
    print('Hi', name)
hello([['Marty', 0.5],
       ['Catherine', 0.9],
       ['Tyler', 0.7],
       ['Pavel', 0.3]])
。。。现在可以这样称呼:

from multiprocessing import Pool

def decorate_func(f):
    def _decorate_func(*args, **kwargs):
        print "I'm decorating"
        return f(*args, **kwargs)
    return _decorate_func

@decorate_func
def actual_func(x):
    return x ** 2

my_swimming_pool = Pool()
result = my_swimming_pool.apply_async(actual_func,(2,))
print result.get()
@parallel(4)
def hello(args):
    #§ from time import sleep     # use '#§' to avoid unnecessary (re)imports in main program
    name, seconds = tuple(args)   # unpack args-list here
    sleep(seconds)
    print('Hi', name)
hello([['Marty', 0.5],
       ['Catherine', 0.9],
       ['Tyler', 0.7],
       ['Pavel', 0.3]])
。。。哪些产出:

This is the parallelized function:
 def func(args):
        from time import sleep
        name, seconds = tuple(args)
        sleep(seconds)
        print('Hi', name)
Launching processes...
Hi Pavel
Hi Marty
Hi Tyler
Hi Catherine

感谢阅读,这是我的第一篇文章。如果您发现任何错误或不良做法,请随时发表评论。我知道这些字符串转换非常肮脏,尽管…

这篇文章似乎表明酸洗装饰对象是很棘手的:是的,我也找到了那个页面。这就是为什么我添加了
functools
包装器。但这似乎没有什么区别。我承认,我真的不明白下面发生了什么,也不明白为什么失败了。如果有人还在找这个帖子,现在就在这里:好的。因此,如果我想对这些东西进行pickle处理,如果我想使用可调用类作为装饰器,那么我将无法使用
@
装饰方法。我必须像实例化类一样使用它。对吗?我相信是对的。或者,您可以通过创建一个简单的非修饰顶级函数来避免对其进行酸洗,该函数只转发到修饰函数。