gpt4 book ai didi

python-3.x - 覆盖的 __setitem__ 调用串行工作,但在 apply_async 调用中中断

转载 作者:行者123 更新时间:2023-12-04 07:47:48 25 4
gpt4 key购买 nike

我一直在与这个问题作斗争一段时间,我终于设法缩小问题范围并创建一个最小的工作示例。
问题的总结是我有一个继承自 dict 的类。以方便解析杂项。输入文件。我已经覆盖了 __setitem__调用以支持对输入文件中的部分进行递归索引(例如 parser['some.section.variable'] 等效于 parser['some']['section']['variable'] )。这对我们来说已经工作了一年多,但是我们在通过这些 Parser 时遇到了一个问题通过 multiprocessing.apply_async 上课称呼。
下面显示的是最小的工作示例 - 显然 __setitem__ call 没有做任何特别的事情,但重要的是它访问一些类属性,如 self.section_delimiter - 这是它打破的地方。它不会在初始调用或串行函数调用中中断。但是当您调用 some_function (它也不做任何事情)使用 apply_async ,它崩溃了。

import multiprocessing as mp
import numpy as np

class Parser(dict):

def __init__(self, file_name : str = None):
print('\t__init__')
super().__init__()
self.section_delimiter = "."

def __setitem__(self, key, value):
print('\t__setitem__')
self.section_delimiter
dict.__setitem__(self, key, value)

def some_function(parser):
pass

if __name__ == "__main__":

print("Initialize creation/setting")
parser = Parser()
parser['x'] = 1

print("Single serial call works fine")
some_function(parser)

print("Parallel async call breaks on line 16?")
pool = mp.Pool(1)
for i in range(1):
pool.apply_async(some_function, (parser,))

pool.close()
pool.join()
如果你运行下面的代码,你会得到下面的输出
Initialize creation/setting
__init__
__setitem__
Single serial call works fine
Parallel async call breaks on line 16?
__setitem__
Process ForkPoolWorker-1:
Traceback (most recent call last):
File "/home/ijw/miniconda3/lib/python3.7/multiprocessing/process.py", line 297, in _bootstrap
self.run()
File "/home/ijw/miniconda3/lib/python3.7/multiprocessing/process.py", line 99, in run
self._target(*self._args, **self._kwargs)
File "/home/ijw/miniconda3/lib/python3.7/multiprocessing/pool.py", line 110, in worker
task = get()
File "/home/ijw/miniconda3/lib/python3.7/multiprocessing/queues.py", line 354, in get
return _ForkingPickler.loads(res)
File "test_apply_async.py", line 13, in __setitem__
self.section_delimiter
AttributeError: 'Parser' object has no attribute 'section_delimiter'
任何帮助是极大的赞赏。我花了相当多的时间来追踪这个错误并重现一个最小的例子。我不仅想修复它,而且还想清楚地填补我对这些 apply_async 的理解上的一些空白。和继承/覆盖的方法相互作用。
如果您需要更多信息,请告诉我。
非常感谢你!
艾萨克

最佳答案

原因
问题的原因是multiprocessing序列化和反序列化您的 Parser对象跨进程边界移动其数据。这是使用 pickle 完成的。 .默认 pickle does not call __init__() 反序列化类时。正因为如此 self.section_delimiter解串器调用 __setitem__() 时未设置恢复字典中的项目,您会收到错误消息:

AttributeError: 'Parser' object has no attribute 'section_delimiter'


仅使用 pickle 而没有多处理会产生相同的错误:
import pickle

parser = Parser()
parser['x'] = 1

data = pickle.dumps(parser)
copy = pickle.loads(data) # Same AttributeError here
反序列化将适用于没有项目且值为 section_delimiter 的对象。将恢复:
import pickle

parser = Parser()
parser.section_delimiter = "|"

data = pickle.dumps(parser)
copy = pickle.loads(data)

print(copy.section_delimiter) # Prints "|"
所以从某种意义上说,你只是不走运泡菜调用 __setitem__()在它恢复您的 Parser 的其余状态之前.
解决方法
您可以通过设置 section_delimiter 来解决此问题。在 __new__()并告诉 pickle 将哪些参数传递给 __new__()通过实现 __getnewargs__() :
def __new__(cls, *args):
self = super(Parser, cls).__new__(cls)
self.section_delimiter = args[0] if args else "."
return self

def __getnewargs__(self):
return (self.section_delimiter,)
__getnewargs__()返回一个参数元组。因为 section_delimiter设置在 __new__() ,不再需要在 __init__()中设置.
这是您的 Parser 的代码更改后的类:
class Parser(dict):

def __init__(self, file_name : str = None):
print('\t__init__')
super().__init__()

def __new__(cls, *args):
self = super(Parser, cls).__new__(cls)
self.section_delimiter = args[0] if args else "."
return self

def __getnewargs__(self):
return (self.section_delimiter,)

def __setitem__(self, key, value):
print('\t__setitem__')
self.section_delimiter
dict.__setitem__(self, key, value)
更简单的解决方案
泡菜调用 __setitem__()的原因在您的 Parser 上对象是因为它是一个字典。如果您的 Parser只是一个恰好实现了 __setitem__()的类和 __getitem__()并且有一个字典来实现这些调用然后pickle不会调用 __setitem__()并且序列化无需额外代码即可工作:
class Parser:

def __init__(self, file_name : str = None):
print('\t__init__')
self.dict = { }
self.section_delimiter = "."

def __setitem__(self, key, value):
print('\t__setitem__')
self.section_delimiter
self.dict[key] = value

def __getitem__(self, key):
return self.dict[key]
所以如果你的 Parser没有其他原因作为字典,我不会在这里使用继承。

关于python-3.x - 覆盖的 __setitem__ 调用串行工作,但在 apply_async 调用中中断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67135111/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com