我正在开发一个使用 lxml 在 python 中同时解析多个 xml 文件的项目。当我初始化进程时,我希望我的主类在将 etree 对象传递给进程之前对 XML 做一些工作,但是我发现当 etree 对象到达新进程时,该类仍然存在,但 XML 消失了在对象中,getroot() 返回 None。
我知道我只能使用队列传递可选取的数据,但我传递给“args”字段内的进程的数据也是这种情况吗?
这是我的代码:
import multiprocessing, multiprocessing.pool, time
from lxml import etree
def compute(tree):
print("Start Process")
print(type(tree)) # Returns <class 'lxml.etree._ElementTree'>
print(id(tree)) # Returns new ID 44637320 as expected
print(tree.getroot()) # Returns None
def pool_init(queue):
# see http://stackoverflow.com/a/3843313/852994
compute.queue = queue
class Main():
def __init__(self):
pass
def main(self):
tree = etree.parse('test.xml')
print(id(tree)) # Returns object ID 43998536
print(tree.getroot()) #Returns <Element SymCLI_ML at 0x29f5dc8>
self.queue = multiprocessing.Queue()
self.pool = multiprocessing.Pool(processes=1, initializer=pool_init, initargs=(self.queue,))
self.pool.apply_async(func=compute, args=(tree,))
time.sleep(10)
if __name__ == '__main__':
Main().main()
非常感谢任何帮助。
更新/回答
根据下一篇文章中的答案,我对其进行了一些修改,并设法让它在不使用字符串 IO 的情况下以低得多的内存占用工作。 etree.tostring 方法返回一个字节数组,它可以被 pickle,然后要 unpickle 字节数组可以被 etree 解析。
import multiprocessing, multiprocessing.pool, time, copyreg
from lxml import etree
def compute(tree):
print("Start Process")
print(type(tree)) # Returns <class 'lxml.etree._ElementTree'>
print(tree.getroot()) # Returns <Element SymCLI_ML at 0x29f5dc8>. Success!
def pool_init(queue):
# see http://stackoverflow.com/a/3843313/852994
compute.queue = queue
def elementtree_unpickler(data):
return etree.parse(BytesIO(data))
def elementtree_pickler(tree):
return elementtree_unpickler, (etree.tostring(tree),)
copyreg.pickle(etree._ElementTree, elementtree_pickler, elementtree_unpickler)
class Main():
def __init__(self):
pass
def main(self):
tree = etree.parse('test.xml')
print(tree.getroot()) #Returns <Element SymCLI_ML at 0x29f5dc8>
self.queue = multiprocessing.Queue()
self.pool = multiprocessing.Pool(processes=1, initializer=pool_init, initargs=(self.queue,))
self.pool.apply_async(func=compute, args=(tree,))
time.sleep(10)
if __name__ == '__main__':
Main().main()
更新 2
在对内存进行了一些基准测试后,我发现传递大对象会导致对象无法通过主进程的垃圾收集来清除。这可能不是小规模的问题,但 etree 对象在内存中的数量级为数百 MB。一旦在语句中使用 XML 对象调用异步任务,如果从主进程中删除该对象,则无法从内存中清除该对象,即使我手动调用垃圾收集也是如此。因此,我已恢复为在主流程中关闭 XML 并将文件名传递给子流程。
最佳答案
使用以下代码为 lxml Element/ElementTree 对象注册简单的 picklers/unpicklers。我过去将其与 lxml 和 zmq 一起使用。
import copy_reg
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from lxml import etree
def element_unpickler(data):
return etree.fromstring(data)
def element_pickler(element):
data = etree.tostring(element)
return element_unpickler, (data,)
copy_reg.pickle(etree._Element, element_pickler, element_unpickler)
def elementtree_unpickler(data):
data = StringIO(data)
return etree.parse(data)
def elementtree_pickler(tree):
data = StringIO()
tree.write(data)
return elementtree_unpickler, (data.getvalue(),)
copy_reg.pickle(etree._ElementTree, elementtree_pickler, elementtree_unpickler)
关于python - 无法将 lxml etree 对象传递给单独的进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25991860/