【发布时间】:2021-02-15 07:32:08
【问题描述】:
在下面的代码中,我使用generate_object 方法在_py 属性内动态创建类的对象。
如果我不使用并发方法,代码可以完美运行。但是,如果我使用来自 concurrent.futures 的并发,我不会得到想要的结果,因为错误提示(除其他外):
_pickle.PicklingError: Can't pickle <class '__main__.Script_0_1'>: attribute lookup Script_0_1 on __main__ failed
在谷歌搜索此错误后,我了解到只有可腌制对象才能作为参数传递给 ProcessPoolExecutor.map(),所以我决定看看如何将我的动态类变成可腌制。
问题是这个问题的所有其他解决方案都以不同的方式创建动态对象(与我在_string_to_object() 中使用的不同)。示例:1 和 2
我非常希望保持现在的动态对象创建方式,因为我的很多真实代码都是基于它的,因此我正在寻找与下面这个玩具代码一起使用的并发解决方案。
代码
import random
import codecs
import re
from concurrent.futures import ProcessPoolExecutor
import multiprocessing
class A:
def __init__(self):
self._py = r'''
class Script_{0}_{1}:
\tdef print_numbers(self):
\t\tprint('Numbers = ', {0}, 'and', {1})
'''
def generate_text(self, name_1, name_2):
py = self._py.format(name_1, name_2)
py = codecs.decode(py, 'unicode_escape')
return py
def generate_object(self, number_1, number_2):
""" Generate an object of the class inside the string self._py """
return self._string_to_object(self.generate_text(number_1, number_2))
def _string_to_object(self, str_class, *args, **kwargs):
""" Transform a program written inside str_class to an object. """
exec(str_class)
class_name = re.search("class (.*):", str_class).group(1).partition("(")[0]
return locals()[class_name](*args, **kwargs)
from functools import partial
print('Single usage')
a = A()
script = a.generate_object(1, 2)
script.print_numbers()
print('Multiprocessing usage')
n_cores = 3
n_calls = 3
def concurrent_function(args):
first_A = args[0]
second_A = args[1]
first_A.print_numbers()
second_A.print_numbers()
with ProcessPoolExecutor(max_workers=n_cores) as executor:
args = ( (A().generate_object(i, i+1), A().generate_object(i+1, i+2)) for i in range(n_calls))
results = executor.map(concurrent_function, args)
【问题讨论】:
-
严格来说问题在于多处理而不是
concurrent.futures本身。如果您将ProcessPoolExecutorClass替换为concurrent.futures中的ThreadPoolExecutor类,则不会有问题。如果确实需要多处理,我建议您删除concurrency标签并添加multiprocessing标签。 -
如果您仔细查看错误消息,您将看到
Can't pickle <class '__main__.Script_0_1'。问题归根结底是Script_0_1类是A类的内部,但正在全局范围内寻找。酸洗在内部类上效果不佳。 -
谢谢@Booboo,你知道如何让内部类在全局范围内可见吗?不幸的是,谷歌搜索不会返回有用的结果。
-
在下面查看我的答案。
标签: python python-3.x multiprocessing concurrent.futures dynamic-class-creation