多处理:使用Win32 API从Python修改Excel单元格

我真的想在这里寻找一个好的解决scheme,也许是我完成的概念或弹性体试图做到这一点是错误的!?

我想让我的代码能够使用我所有的核心。 在代码中,我正在使用Win32 API修改Excel单元格。 我写了一个小的xls-Class,它可以检查所需的文件是否已经打开(或者打开它),并将值设置为单元格。 我剥离的代码如下所示:

#!/usr/bin/env python # -*- coding: utf-8 -*- import os import win32com.client as win32 from multiprocessing import Pool from time import sleep class xls: excel = None filename = None wb = None ws = None def __init__(self, file): self.filename = file def getNumOpenWorkbooks(self): return self.excel.Workbooks.Count def openExcelOrActivateWb(self): self.excel = win32.gencache.EnsureDispatch('Excel.Application') # Check whether one of the open files is the desired file (self.filename) if self.getNumOpenWorkbooks() > 0: for i in range(self.getNumOpenWorkbooks()): if self.excel.Workbooks.Item(i+1).Name == os.path.basename(self.filename): self.wb = self.excel.Workbooks.Item(i+1) break else: self.wb = self.excel.Workbooks.Open(self.filename) def setCell(self, row, col, val): self.ws.Cells(row, col).Value = val def setLastWorksheet(self): self.ws = self.wb.Worksheets(self.wb.Worksheets.Count) if __name__ == '__main__': dat = zip(range(1, 11), [1]*10) # Create Object xls = xls('blaa.xls') xls.openExcelOrActivateWb() xls.setLastWorksheet() for (row, col) in dat: # Calculate some value here (only depending on row,col): # val = some_func(row, col) val = 'test' xls.setCell(row, col, val) 

现在,因为循环只依赖于两个迭代variables,所以我想让它在许多内核上并行运行。 所以我听说过线程和多处理,但后者似乎更容易,所以我给了它一个去。

所以我改变了这样的代码:

 import os import win32com.client as win32 from multiprocessing import Pool from time import sleep class xls: ### CLASS_DEFINITION LIKE BEFORE ### ''' Define Multiprocessing Worker ''' def multiWorker((row, col)): xls.setCell(row, col, 'test') if __name__ == '__main__': # Create Object xls = xls('StockDatabase.xlsm') xls.openExcelOrActivateWb() xls.setLastWorksheet() dat = zip(range(1, 11), [1]*10) p = Pool() p.map(multiWorker, dat) 

似乎没有工作,因为经过一些阅读,多处理开始新的进程,因此xls是不知道的工人。

不幸的是,我不能将xls作为第三个parameter passing给它们,因为Win32不能被腌制:(像这样:

 def multiWorker((row, col, xls)): xls.setCell(row, col, 'test') if __name__ == '__main__': # Create Object xls = xls('StockDatabase.xlsm') xls.openExcelOrActivateWb() xls.setLastWorksheet() dat = zip(range(1, 11), [1]*10, [xls]*10) p = Pool() p.map(multiWorker, dat) 

唯一的办法就是在multiWorker的定义之前,为每个进程初始化Win32:

 # Create Object xls = xls('StockDatabase.xlsm') xls.openExcelOrActivateWb() xls.setLastWorksheet() def multiWorker((row, col, xls)): xls.setCell(row, col, 'test') if __name__ == '__main__': dat = zip(range(1, 11), [1]*10, [xls]*10) p = Pool() p.map(multiWorker, dat) 

但我不喜欢它,因为我的xls的构造函数有一些更多的逻辑,它会自动尝试find已知的标题子string的列标识符…所以这是多一点努力,然后想要的(我不认为每个进程应该真的打开它自己的Win32 COM接口),这也给我一个错误,因为gencache.EnsureDispatch可能无法经常调用….

该怎么办? 解决scheme如何? 谢谢!!

虽然Excel在重新计算电子表格时可以使用多个核心,但其编程接口与单线程的UI模型紧密相关。 活动工作簿,工作表和select都是单例对象; 这就是为什么你不能在使用COM(或VBA)驱动它的同时与Excel UI进行交互。

TL;博士

Excel不能这样工作。

Interesting Posts